好好学习 天天向上!遇到bug,不要慌!
文章目录
webpack学习之旅
大一统的模块化规范--ES6模块化
1.node.js中通过babel体验ES6模块化
2.ES6模块化基本语法
设置默认导入和导出
按需导入导出
直接导入并执行代码
webpack正文
创建列表隔行变色项目
webpack基本使用
安装和配置webpack文件
配置webpack的打包入口和出口
设置webpack自动打包
配置html-webpack-plugin
配置自动打包参数
webpack加载器和插件
1.打包处理css文件
2.打包处理less文件
3.打包处理scss文件
4.将css提取到单独文件
5.配置postCss自动添加css兼容性代码
6.开启CSS压缩
7.打包样式表中的图片
8.打包其他文件
9.打包js文件的高级语法
10.文件输出到相应文件夹
11.压缩html代码
完整配置
性能优化配置
使用HMR优化打包构建速度
使用source-map优化代码调试
使用oneOf优化打包构建速度
总结
大一统的模块化规范–ES6模块化
1.node.js中通过babel体验ES6模块化
打开终端,输入命令:
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
安装完毕之后,输入命令安装:
npm install --save @babel/polyfill
项目根目录创建babel.config.js文件,内部代码如下
const presets = [ ["@babel/env",{ targets:{ edge:"17", firefox:"60", chrome:"67", safari:"11.1" } }] ] //暴露 module.exports = { presets }
创建一个测试文件(随便打印点东西就行)
//创建了index.js文件 console.log('ok');
在终端执行命令
npx babel-node ./index.js
2.ES6模块化基本语法 设置默认导入和导出 默认导出语法:
export default { 成员A, 成员B, ... }
使用方法
let num = 10; let cnt = 2; export default { num }//只暴露num
默认导入语法 注意:接收名称是自己定的,在当前文件中使用的名称
// import 接收名称 from "模块标识符" import index from './index.js' console.log(index);// { num: 10 }
注意:在一个模块中,只允许使用export default向外默认暴露一次成员,不能写多个export default,否则会报错 小tip: 如果在一个模块中没有向外暴露成员,其他模块引入该模块时将会得到一个空对象 在导出的函数中能够读取到没有被导出的值
//index.js文件 let num = 10; let cnt = 2; function showCnt() { console.log(cnt); } export default { num, showCnt, }//这里的cnt并没有被导出 //m1.js文件 import index from './index.js' index.showCnt();// 2
按需导入导出 按需导出语法
export let age = 998; export let name = 'ljc'
按需导入语法
import {age, name} from './m2.js' console.log(name + age);// ljc19
导出名需要和属性名相对应,可以通过as来设置别名,例如:
import {age ,name as nickname} from './m2.js' console.log(nickname + age);//ljc19
这样就实现了设置别名,设置别名后原先的名字就不能用了 默认和按需两种方法共存
//m2.js文件 导出 export let age = 19; export let name = 'ljc' let sex = '男' export default { sex } //m1.js文件 导入 import m2, {age, name as nickname} from './m2.js' console.log(m2);// { sex: '男' } console.log(nickname + age); //ljc19
注意:一个模块中既可以按需导入导出也可以默认导入导出 直接导入并执行代码
import "./m2.js";
直接执行,不接收模块中暴露成员
webpack正文
webpack提供了模块化支持,代码压缩混淆,解决js兼容问题,性能优化等特性,提高了开发效率和项目的可维护性
从实战中学习更加有意义,之前看了很多个讲师的视频,有点枯燥无厘头了,听完了完全不知道学到了什么
创建列表隔行变色项目
创建项目目录并初始化
终端运行npm init -y命令,作用是初始化包管理器配置文件package.json
项目目录
src --> index.html //新建src文件夹下创建index文件
创建首页及js文件 写一下隔行变色项目的结构,然后在src目录下创建index.js文件 安装jQuery 终端输入命令npm install jquery -S 注意:这里是jquery不是jQuery 导入jQuery
import $ from "jquery"; $(function(){ $('li:odd').css('backgroundColor','pink'); $('li:even').css('backgroundColor','blue') })
此时项目运行会报错,原先是第一行的代码属于ES6语法,浏览器存在兼容性问题
因此我们通过webpack将代码转化为浏览器能够兼容的代码
webpack基本使用
这部分学了n遍了,所以笔记有点预知未来的感觉,希望这次能有好结果
安装和配置webpack文件
运行 npm install webpack webpack-cli –D 命令,安装 webpack 相关的包
在根目录下创建webpack.config.js文件,在执行webpack时会优先从用户自定义的配置文件中调用
在配置文件中写以下代码,
注意:mode的意思是文件导出格式,有生产模式和开发模式,生产模式的代码会进行压缩,去掉所有空格之类的,代码文件小,但是不利于我们的学习,因此在学习的时候还是设置为开发模式
这个配置文件在后面的学习中还会添加大量的内容
module.exports = { mode:"development"//可以设置为development(开发模式),production(发布模式) }
修改package.json文件,添加一点东西
//未添加 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", } //添加后 "scripts":{ "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack" }
script对象下的配置属性,可以通过 npm run 运行
例如:npm run dev,运行dev启动webpack服务进行项目打包
如果这里报错
请检查两个index文件是否存在于src目录下
可能是安装jquery的时候打了大写的Q
在执行完命令后,我们会发现项目目录下多了个dist文件夹,这个文件夹存放的是通过webpack打包后的文件,也就是我们这个项目中转为为浏览器兼容的代码,因此我们将这个文件引入我们的index.html文件即可。
通俗点说就是,这个是转化好的文件,实现的效果和我们先前写的一样,但是这个文件实现了兼容,所以我们引用的时候,引用这个文件就好
配置webpack的打包入口和出口
默认会将src/index.js 作为默认的打包入口js文件,默认会将dist/main.js 作为默认的打包输出js文件
我们可以通过配置之前所创建的配置文件(webpack.config.js)来自定义出入口文件
const path = require('path');//导入node.js中操作路径的模块 module.exports = { entry: path.join(__dirname,"./src/index.js"),//设置入口文件路径,绝对路径 //配置出口文件 output:{ path:path.join(__dirname,"./dist"),//设置路径 filename:"res.js"//设置导出文件名称 }, //模式选择 mode: "development" }
在上面的代码中我们引用了node中的path模块,path.join()用于连接路径,会正确地使用当前系统的路径分隔符,也就是绝对路径
注意:在上面代码中的出入口文件路径,需要根据自己的文件目录来写,cv大概率报错噢
设置webpack自动打包
我们每次修改代码都要重新打包,太麻烦了,因此需要自动打包
安装自动打包工具
npm install webpack-dev-server -D
修改package.json文件中的dev指令,也就是我们自己先前添加的那个属性(4.x版本)
//修改前 "dev": "webpack" //修改后 "dev": "webpack-dev-server"
注意:如果使用的webpack版本是5.x的,则需要将上面的配置文件修改为"dev": "webpack server",否则报错。或者使用npx webpack serve运行
webpack版本可以通过终端命令webpack -v查看
运行npm run dev,进行打包
在运行结果中,有相关的信息
i 「wds」: Project is running at http://localhost:8080/ i 「wds」: webpack output is served from /
第一行是服务器的地址,我们可以访问这个地址来运行我们的网页
第二行是文件的输出路径为/,这个意思是存放到了服务器的根目录中
注意:webpack server自动打包的输出文件,默认放到了服务器的根目录中
这些地址文件目录,我们都可以通过配置文件来更改
记得要将js文件引入页面中,通过第4步知道,文件存放于服务器的根目录中,因此
<script src="/bundle.js"></script>
webpack server 会启动一个实时打包的http服务器
输出文件放在了服务器中,在项目目录中看不见,不太友好
配置html-webpack-plugin
利用这个插件生成一个预览页面,解决我们在访问8080时,直接看到的不是页面的问题
安装包html-webpack-plugin
npm install html-webpack-plugin -D
修改配置文件 引入下载好的包,配置相应文件
//导入包 const HtmlWebpackPlugin = require("html-webpack-plugin"); //创建对象 const htmlPlugin = new HtmlWebpackPlugin({ //设置生成预览页面的模板文件 template:"./src/index.html", //设置生成的预览页面名称 filename:"index.html" })
在这个对象中,新增plugins属性,把实例化好的对象放进去 注意:plugins有s
module.exports = { ... plugins:[ htmlPlugin ] }
在很多视频中都是先把这个配置文件的大体写出来的,这个视频不是,有点不习惯 配置自动打包参数 再次更改package.json文件中的dev属性
"dev": "webpack server --open --host 127.0.0.1 --port 9999"
--open是自动启动 --host是指定地址 --port是指定服务端口号 也有另一种方法是通过修改webpack.config.js配置文件 添加一个devServer属性,配置相关信息
module.exports = { ... output:{}, devServer: { port: 3030, publicPath: './dist' }, }
webpack加载器和插件
这个真的超级常用,默认情况下,webpack只能打包js文件,如果想要打包非js文件,需要调用loader加载器才能打包
less-loader 可以打包处理 .less 相关的文件
sass-loader 可以打包处理 .scss 相关的文件
url-loader 可以打包处理 css 中与 url 路径相关的文件
loader调用过程
注意:使用npm run dev是为了实时观看页面,但是不会生成文件,只是一个预览效果,所以我们要生成文件的时候需要使用webpack命令打包 1.打包处理css文件 安装loader包
npm install style-loader css-loader -D
在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下内容如下
output: {...}, module: { rules :[ { test:/\.css$/,//匹配css文件,匹配成功使用下面的loader use:[ 'style-loader', 'css-loader' ] } ] },
注意: use 数组中指定的 loader 顺序是固定的 多个 loader 的调用顺序是:从后往前调用 loader的摆放顺序出错可能会报错 2.打包处理less文件 打开终端,运行命令,下载less-loader
npm i less-loader less -D
添加配置文件,匹配less文件,使用loader加载
{ test:/\.less$/, use:[ 'style-loader', 'css-loader', 'less-loader' ] }
其实这部分理解了之后会很简单,上面这段代码我们可以接在处理css文件后面继续写,也就是可以理解为rules数组存放大量的规则,每个规则都是一个包含test和use的对象,这样就很清晰了 3.打包处理scss文件 这一部分内容我是没有成功的,查了很多资料都没有成功,视频课程讲解的webpack版本是4.x,我用的是5.x,有些东西被弃用了,安装不成功,所以可以跳过这部分 打开终端,运行命令,下包
npm i sass-loader node-sass -D
配置规则,继续在rules中添加配置
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader' ] }
4.将css提取到单独文件 打开终端,运行命令4.将css提取到单独文件 打开终端,运行命令
npm install --save-dev mini-css-extract-plugin
引入
//css提取成单独文件 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
更改用户配置
//更改css相关配置 { test: /\.css$/, use: [ //这里改以下 MiniCssExtractPlugin.loader, 'css-loader' ] } //创建插件对象 plugins: [ htmlPlugin, new MiniCssExtractPlugin() ]
5.配置postCss自动添加css兼容性代码 这个是真的香 打开终端,运行命令,安装loader
npm install postcss-loader autoprefixer -D
在项目根目录创建并配置postcss.config.js文件
const autoprefixer = require("autoprefixer");//引入 module.exports = { plugins:[ autoprefixer ] }
配置规则,更改rules数组
//更改css相关配置 { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', //在这里添加新的loader规则 'postcss-loader' ] }
6.开启CSS压缩 webpack4使用optimize-css-assets-webpack-plugin插件,方法和上面的都一样,引入,然后创建实例对象 下面记录webpack5的使用 打开终端,安装插件
npm install css-minimizer-webpack-plugin --save-dev
引入创建插件对象
//引入 const CSSMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin'); ... //在plugins数组中创建 plugins: [ htmlPlugin, new MiniCssExtractPlugin(), new CSSMinimizerWebpackPlugin() ]
7.打包样式表中的图片 在样式表css中有时候会设置背景图片和设置字体文件,一样需要loader进行处理 使用url-loader和file-loader来处理打包图片文件以及字体文件 打开终端,安装loader
npm install url-loader file-loader -D
配置loader文件
{ test:/\.(jpg|png|gif|bmp|ttf|eot|svg|woff|woff2)$/, use:"url-loader?limit= 8 * 1024" }
注意: css样式表中的文件才会通过这个插件被打包 ?limit后面填的是字节大小,只有小于这个数的才会被直接打包,存放的是你自己写的那个路径,大于这个数,会转化为base64路径 8.打包其他文件 在上一步中已经安装好了loader,添加配置文件即可
{ exclude:/\.(css|js|html|less|jpg|png|gif|bmp|ttf|eot|svg|woff|woff2)$/,//排除这些文件 loader:'file-loader', options: { name: '[hash:10].[ext]'//缩短文件名 } }
9.打包js文件的高级语法 打开终端,安装babel
npm install babel-loader @babel/core @babel/runtime -D
安装babel语法插件包
npm install @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
在项目根目录创建并配置babel.config.js文件
module.exports = { presets:["@babel/preset-env"], plugins:[ "@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties" ] }
更改用户配置文件中的rules,数组下继续添加
{ test:/\.js$/, use:"babel-loader", //use表示该文件类型需要调用的loader exclude:/node_modules/ }
10.文件输出到相应文件夹 css文件: 在实例化的时候添加参数配置
new MiniCssExtrac: 'css/[name].css' })
图片文件: 在rules中添加options配置内容
{ test: /\.(jpg|png|gif|bmp|ttf|eot|svg|woff|woff2)$/, use: [{ loader: "url-loader", options: { limit: 8 * 1024, name: '[hash:10].[ext]', esModule: false, outputPath: './imgs', publicPath: './imgs' // 开发环境配置 } }] }
11.压缩html代码 和上面一样添加插件配置 其实就是配置删除空格和注释
// const htmlPlugin = new HtmlWebpackPlugin({ //设置生成预览页面的模板文件 template: "./src/index.html", //设置生成的预览页面名称 filename: "index.html", //添加压缩代码 collapseWhitespace: true, removeComments: true, options: { esModule: false } })
完整配置
const path = require('path'); //导入node.js中操作路径的模块 //导入html包 const HtmlWebpackPlugin = require("html-webpack-plugin"); //创建对象 const htmlPlugin = new HtmlWebpackPlugin({ //设置生成预览页面的模板文件 template: "./src/index.html", //设置生成的预览页面名称 filename: "index.html", //压缩代码 // collapseWhitespace:true, // removeComments:true, options: { esModule: false } }) //css提取成单独文件 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //css压缩 const CSSMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin'); module.exports = { entry: path.join(__dirname, "./src/js/index.js"), //设置入口文件路径 //配置出口文件 output: { path: path.join(__dirname, "./dist"), //设置路径 filename: "js/bundle.js" //设置导出文件名 }, module: { rules: [{ //处理css test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ] }, { //处理less test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader' ] }, { //处理scss,不用 test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader' ] }, { //处理图片 test: /\.(jpg|png|gif|bmp|ttf|eot|svg|woff|woff2)$/, use: [{ loader: "url-loader", options: { limit: 8 * 1024, name: '[hash:10].[ext]', esModule: false, outputPath: './imgs', publicPath: '../imgs' // 公共路径 } }] }, { //处理其他资源 exclude: /\.(css|js|html|less|jpg|png|gif|bmp|ttf|eot|svg|woff|woff2)$/, loader: 'file-loader', options: { name: '[hash:10].[ext]', outputPath: 'media', publicPath: '../media' // 开发配置或者就行 } }, { //转化高级js代码 test: /\.js$/, use: "babel-loader", exclude: /node_modules/ } ] }, devServer: { //服务器相关配置 port: 9999, open: true }, target: 'web',//实时刷新 devtool: "eval-source-map", //后面会写到 plugins: [ htmlPlugin, new MiniCssExtractPlugin({ filename: 'css/[name].css' }), new CSSMinimizerWebpackPlugin() ], //模式选择 mode: "development" }
性能优化配置
使用HMR优化打包构建速度
HMR对html,css,js都有不同的配置,js,和html文件默认是不使用HMR功能的
问题:如果我们只是修改了样式文件,没有被修改过的js等文件也会因为页面的刷新而被重新加载一次,所有的代码被重新执行一次即,这种情况往往不是我们想要的效果
解决方法:使用HMR功能来完成这个需求。它的作用是当一个模块发生变化,只会重新打包这一个模块,而不是打包加载所有模块`,极大提升构建速度
devServer: { port: 9999, open: true, // 开启HMR功能 // 新配置要想生效,必须重新webpack服务 // 重新执行npx webpack server指令 hot: true }
HMR功能开启后当我们修改样式文件,我们在控制台上可以发现此时只有该样式文件被重新加载刷新了,其他的文件不会重新输出。 注意: 对于html文件不需要做HMR功能,因为只有一个html文件,只要修改了,必定重新加载 js文件用HMR感觉不太友好
if (module.hot) { // 一旦 module.hot 为true,说明开启了HMR功能。 // 额外添加下面的JS代码 // 让HMR功能代码在此JS文件修改时生效 module.hot.accept('./print.js', function() { // 方法会监听 print.js 文件的变化 // 一旦发生变化,其他模块不会重新打包构建。 // 会执行后面的回调函数 print();//这是一个js文件下的函数 }); }
使用source-map优化代码调试 source-map是一种提供源代码到构建后代码映射技术,简单来说就是配置文件报错的提示方式,在配置文件中配置devtool即可
devServer: {}, target: 'web',//实时刷新 devtool: "eval-source-map"
直接配置在export对象下
对于devtool配置有两种方案,内联和外联
内联和外部的区别:
外联生成了文件,内联不生成
内联构建速度更快,但是文件体积会更大
这里的配置选项相当多,可以直接查看devtool
开发环境
需要考虑速度快,调试更友好
速度快(eval>inline>cheap>…) eval-cheap-souce-map和eval-source-map
调试更友好 souce-map和cheap-module-souce-map和cheap-souce-map
推荐使用:eval-source-map(√) / eval-cheap-module-source-map
生产环境
内联会让代码体积变大,所以在生产环境不用内联
source-map 能够提供错误代码准确信息和源代码的错误位置
cheap-module-souce-map 能够提供错误代码准确信息和源代码的错误位置只能精确的行而不是列
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
推荐使用:source-map(√)
使用oneOf优化打包构建速度
在我们之前写的rules中,每一个文件都会被所有的规则判断一遍,这样的操作是没有必要的
因此我们使用oneOf来解决这个问题,优化我们的打包代码
在所有的rules外层用一个oneof数组包裹,也就是将我们之前写的规则放在oneof数组中,oneof数组放在rules数组对象中
rules: [ { oneOf:[...相关loader] } ]
如果有需要匹配两次以上的,需要将对应的loader放在外部,和oneOf数组存在的对象同级,可以通过添加enforce: 'pre'属性,优先执行这个规则