相信你或多或少也听说过webpack、gulp等这些前端构建工具。近年来webpack越来越火,可以说成为了前端开发者必备的工具。 如果你有接触果vue或者react项目,我想你应该对它有所了解,其单单只是用来打包?这可不止,下面让斌果带你慢慢了解和使用webpack吧!(由于我在写这篇文章的时候webpack最新版本是4.3 如果某些配置有变更,请自行查看官方最新文档。)
一: 初识webpack 1.1 什么是webpack? 这个我直接引用官方文档的介绍: Webpack 是当下最热门的前端资源模块化 管理和打包 工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分割,等到实际需要的时候再异步加载。 在这里我就不多介绍了,百度一搜一大堆介绍,本文章主要是介绍webpack怎么用。
1.2 安装webpack 因为webpack是基于nodejs实现的,所以要安装webpack,先得安装node nodejs的中文网址:http:/// 下载完node之后,直接傻瓜式安装就好,接着,我们打开命令行,看下node和vue的版本 当输出版本号时,证明安装成功。(建议node尽量8.0版本以上 npm尽量6.0版本以上)。 这里我比较建议用淘宝的镜像,npm在国内实在是太慢了。 npm install -g cnpm --registry=https://registry.npm. 当然,如果你网速快的话,用npm命令也没关系。 首先,我们创建一个空的文件夹,就起名叫webpack吧,然后执行一下初始化命令 npm init -y 接着安装webpack和webpack-cli(建议局部安装,全局安装webpack可能会导致webpack版本不同而无法正常使用) npm install webpack webpack-cli --save-dev 或者 cnpm install webpack webpack-cli -save-dev 由于我电脑网速比较慢,以下我全部用cnpm命令
1.3 webpack的初始化配置 首先我们在本件的一级目录下创建一个src文件,src文件夹内再创建一个inde.js文件和一个index.html文件 src/html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>初始化配置</title> </head> <body> <div id="root">hello word!</div> <script src="index.js"></script> </body> </html>src/js: var root = document.getElementById('root'); var text = document.createElement('div'); text.innerText = "斌果欢迎你!" root.append(text);当然packagej.json文件也要配置一下。 删除“main”入口配置添加"private"为true { "name": "webpack", "version": "1.0.0", "description": "", "private":true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.30.0", "webpack-cli": "^3.3.0" } } 这样做主要是为了让你的项目私有化,防止意外发布你的代码。 接着,在一级目录下再创建一个 webpack.config.js 这个主要是webpack的配置文件 webpack.config.js const path = require('path'); module.exports = { entry:'./src/index.js', //打包文件的入口 output:{ //打包文件的出口 filename:'bundle.js', //打包后的文件名 path:path.resolve(__dirname,'dist') //打包后文件存放的位置 } } webpack使用的是common.js规范的语法,在webpack.config.js中设置了webpack打包的基本配置。 接着我们在控制台中直接运行 npx webpack 再回到项目中的,发现多了一个dist文件夹。里面有一个打包后的bundle.js文件,就这样我们就将这个简单的项目打包成功了。 你或许会问npx是什么,我怎么从来没有用过,可能你一直都是在用npm run ** npx其实就是直接运行node_modules中的配置 当然你也可以在package.json中配置一下,像这样 配置之后你运用npm run bundle 效果其实和运行 npx webpack是一样,其最终还是运行npx webpack的命令.。
1.4 webpack的初次使用 在上一小节,你可能会觉得,这webpack打包完后好像也并没有用,到底webpack是用来干嘛的?这一小节,就带你用用webpak。 继续上一节,我们在src下继续创建header.js,connent.js、footer.js这三个文件.。 src/header.js var root = document.getElementById('root'); var text = document.createElement('div'); text.innerText = "头部内容" root.append(text); src/content.js var root = document.getElementById('root'); var text = document.createElement('div'); text.innerText = "中部内容" root.append(text); src/footer.js var root = document.getElementById('root'); var text = document.createElement('div'); text.innerText = "尾部内容" root.append(text); 如果我们想将这三个js同时引入到index.html我们就必须要这样写 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>简单实例</title> </head> <body> <div id="root">hello word!</div> <script src="header.js"></script> <script src="content.js"></script> <script src="footer.js"></script> </body> </html> 这样我们要引入3个js 对性能的消耗是十分巨大的 ,加入有100个这样的js文件,那我们不就要引入100个js? 于是我们想可以可以只引入一个inde.js。就像下面那样: 我们将src中引入的header、content、footer、index全部使用esModule语法如下 function header(){ var root = document.getElementById('root'); var text = document.createElement('div'); text.innerText = "头部内容" root.append(text); } export default header; import header from './header' import content from './content' import footer from './footer' header(); content(); footer(); 当我们打开html,就会发现报了个错! 这是为什么呢? 因为浏览器根本都不认识import是什么,是不认识es、common这些语言的,但是,webpack认识啊! 我们直接运行 npx webpack 然后我们index.html中直接引入打包后的bundle.js,就会神奇的发现他成功运行而且没报错。 因为webpack已经帮我们将esModule语法转化为浏览器能识别的法语告诉浏览器我要引入这三个js。
二、webpack的核心常用配置
2.1 HtmlWebpackPlugin 在上一章可以看到,当我们打包完成后,要自己将打包后的js代码引入到html文件中,这显得不那么智能。 HtmlWebpackPlugin插件做得工作就是在打包完成后,自动生成一个html文件,并将打包生成的js自动引入到html文件中。 首先安装这个插件: npm install html-webpack-plugin -D 安装完成后,我们需要在webpack.config.js中配置一下: const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); // 导入插件 module.exports = { entry:'./src/index.js', //打包文件的入口 plugins:[ new HtmlWebpackPlugin({ template: 'src/index.html' // 其会根据该html文件作模板自动导入打包后的js ,然 }) // 后将生成的html放到出口文件夹中 ], output:{ //打包文件的出口 filename:'bundle.js', //打包后的文件名 path:path.resolve(__dirname,'dist') //打包后文件存放的位置 } } 这样的话就无需要我们自己配置html 其会在出口文件夹自动生成打包后的html。 运行npx webpack后
2.2 CleanWebpackPlugin 当我们每次打包完文件后,都会生成一个新的dist文件覆盖上一个dist文件,但如果上个dist中有一些其他的文件没有被覆盖掉,就会成为杂鱼,当我们多次打包后,杂鱼可能就会变得越来越多,严重影响开发。 CleanWebpackPlugin插件做得就是每次打包时都会删除上一次打包的文件,而不是覆盖,这就使得每一次打包后的文件都是崭新的,而不会混合上一次遗留的文件。 首先安装这个插件: npm install clean-webpack-plugin -D 接着配置一下webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); // 导入插件 module.exports = { entry:'./src/index.js', //打包文件的入口 plugins:[ new HtmlWebpackPlugin({ template: 'src/index.html' //其会根据该html文件作模板自动导入打包后的js ,然 }), //后将生成的html放到出口文件夹中 new CleanWebpackPlugin() // 配置删除的文件 ], output:{ //打包文件的出口 filename:'bundle.js', //打包后的文件名 path:path.resolve(__dirname,'dist') //打包后文件存放的位置 } } 运行npx webpack 后
2.3 Mode配置 当我们每次打包的时候都会提示如下警告 这到底是什么呢? 其实就是叫我们设置工作状态,既mode mode有两种状态,主要是用于表明当前开发状态! development: 开发模式 打包后代码没有被压缩 production: 生产模式 打包后代码被压缩为一行
2.4 SourceMap配置 当我们的代码报错时,调试工具显示的是打包后js的报错行数,这样我们很难找到原代码到底在哪报错。 SourceMap主要用于开发模式下使用,其能展现出打包后文件与源文件的映射关系。 这到底是怎么用的呢? 查看下官方文档可以看到有这么多参数,不急,慢慢来解释: 表示性能较好,-表示性能较差 eval、inline、cheap、module等可以自由组合使用 none:不产生映射,速度最快。 eval:执行速度也很快,性能最好,当时代码提示不全面。 source-map: 把映射文件生成到独立的文件,一般不建议使用,打包速度过慢。 -inline-: 不单独生成文件,将生成的映射文件以base64的格式插入到打包后的js文件中,精确显示错误位置 -cheap-:代码只显示出错代码在第几行,不精确显示具体位置,有效提高打包效率。 -module-:不仅提示自己的业务代码是否出错,还提示第三方模块中的代码是否出错。 在开发环境中,建议使用:cheap-module-eval-source-map 在生产环境中,上线代码一般不设置devtool。如果想知道报错位置,建议使用:cheap-module-source-map 因为我们主要在开发环境下,所以我们使用cheap-module-eval-source-map
2.5资源管理配置 2.5.1 加载css、sass、css前缀 首先我们先在src下创建一个index.css src/index.css: body{ background-color: blue; } 接着我们在src中的index.js中导入css文件 src/index.js: import header from './header' import content from './content' import footer from './footer' import './index.css' // 引入css文件 header(); content(); footer(); 由于webpack是无法识别以css后缀的文件,所以我们要引入loader(翻译官)帮我们解析css. 我们需要安装一下loader: npm install style-loader css-loader --save-dev 安装完成后,我们还需要在webpack.config.js中配置一下: webpack.config.js: const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); //导入插件 module.exports = { mode:'development', devtool:'cheap-module-eval-source-map', // 映射 entry:'./src/index.js', //打包文件的入口 module:{ rules:[ { test:/\.css$/, // 配置以css结尾的文件 use:[ // 添加翻译官,顺序为由下向上,由右向左 'style-loader', 'css-loader' ] } ] }, plugins:[ new HtmlWebpackPlugin({ template: 'src/index.html' //其会根据该html文件作模板自动导入打包后的js ,然 }), //后将生成的html放到出口文件夹中 new CleanWebpackPlugin() //配置删除的文件 ], output:{ //打包文件的出口 filename:'bundle.js', //打包后的文件名 path:path.resolve(__dirname,'dist') //打包后文件存放的位置 } } 由于css翻译官解析后webpack还是不认识css文件,还需要使用style翻译官去处理一下才能被webpack所解析。 运行npx webpack打包后,我们直接打开dist下的index.html文件,就可以看到css文件已经被成功引入. 加载sass也是类似,只需要添加一个sass-loader、node-sass就可以了!在这里就不加演示。 { test:/\.scss$/, use:[ 'style-loader', 'css-loader', 'sass-loader' ] } 而加前缀的话,在不同浏览器前缀各有不同。
在这里我们只需要引入postcss-loader和autoprefixer就可以了 安装一下 : npm install postcss-loader autoprefixer --save-dev 在根目录下创建一个postcss.config.js文件 postcss.config.js: module.exports = { plugins:[ require('autoprefixer') ] } 接着在webpack.config.js修改一下loader { test:/\.css$/, //配置以css结尾的文件 use:[ //添加翻译官,顺序为由下向上,由右向左 'style-loader', 'css-loader', 'postcss-loader' // 配置前缀 ] }, { test:/\.scss$/, use:[ 'style-loader', 'css-loader', 'sass-loader', //配置sass翻译官 'postcss-loader' // 配置前缀 ] } 但是如果我们要在sass中再引入sass 这就会出错,因为再引入的sass文件没有经过翻译官翻译. 所以,我们还需要配置一下sass { test:/\.scss$/, use:[ 'style-loader', { loader:'css-loader', options:{ importLoader:2,// 如果sass文件还引入其他sass,另一个会重新从下向上开始解析 modules:true // 开启css模块打包 } }, 'sass-loader', //配置sass翻译官 'postcss-loader' //配置前缀 ] } 2.5.2 加载图片、字体 加载图片主要使用两个loader分别为 file-loader url-loader 其两个loader都是解析图片。不过url-loader可以将小图片转化成base64的格式存放到打包后的css中 我们一般都是使用url-loader 这样处理小图片的时候就可以更快加载而无需引用原图片地址。 安装: npm install url-loader file-loader -D 配置: webpack.config.js: { test:/\.(png|jpg|gif|svg)$/, use:[ { loader:'url-loader', options:{ name:'[name].[ext]', outputPath:'images/', limit:8096 // 小于4K的图片转化为base64存放到打包后的js中 } } ] } 因为file-loader和url-loader可以加载任何文件,所以我们也可以用其来加载字体文件 webpack.config.js: { test:/\.(woff|woff2|eot|ttf|otf)&/, use:[ 'file-loader' ] } 2.5.3加载es6语法 由于很多低版本的浏览器都不支持ES6语法,所以,我们需要将ES6语言转换为ES5的语法。 在以前我都是用babel-preset-es2015转换的,现在官方推荐使用babel-preset-env转换,我们就使用官方推荐的来转换吧. 由于babel只转换语法,需要使用babel-polyfill来支持新语法像promise这些。 首先我们先安装依赖 npm install babel-loader @babel/core babel-preset-env @babel/polyfill -D 接着我们在webpack.config.js中配置一下 webpack.config.js: { test: /\.js$/, exclude: /node_modules/,//不需要对第三方模块进行转换,耗费性能 use:[ 'babel-loader' ] }, 因为bebel配置的信息太多了,我们可以在根目录下创建一个.babelrc文件 .babelrc: { "presets":[["babel-preset-env",{ targets:{ //这个项目在大于什么版本上不转换为es5 edge:"17", firfox:"60", chrome:"67", safari:"11.1", }, useBuiltIns:'usage' }]] } 最后我们还需要在index.js中引入polyfill: src/index.js: import "@babel/polyfill"; 但是如果在第三方模块时使用@babel/polyfill会污染全局变量,所以我们可以使用runtime-transform插件 其与polifill类似,但不会污染全局变量其用法和上面差不多 我们安装一下: npm install babel-runtime babel-plugin-transform-runtime -D 在.babelrc中添加配置就可以了。 { "plugins": ["transform-runtime"] } 更多的配置可以参考官网文档:babeljs.io/setup
2.6配置多个出入口文件 在目前,我们一直以一个index.js为入口,bundle.js文件为出口,但随着程序的增加,我们可能需要输出多个bundle文件。 这个很简单,我们新建一个print.js文件,并添加一些逻辑 import Header from './header' Header(); console.log('这是打包的第二入口文件'); 然后我们只需要修改一下webpack.config.js中的出入口配置就可以了。 webpack.config.js: entry:{ main:'./src/index.js', //打包入口文件 print:'./src/print.js' }, ...... output:{ filename:'[name].js', //打包后的文件名 path:path.resolve(__dirname,'dist') //打包后文件存放的位置 }
2.7热更新配置 我们每次打包的时候,都需要手动运行npm run bundle 或者npx webpack这显得什么繁琐。 下面介绍几个配置,可以在代码发生变化后自动编译代码。 2.7.1观察模式watch 我们只需要在package.json文件中添加一个用于启动观察模式的npm script脚本就可以了; { "name": "webpack", "version": "1.0.0", "description": "", "private": true, "scripts": { "bundle": "webpack", "watch": "webpack --watch" // 添加启动脚本 }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "autoprefixer": "^9.5.1", "clean-webpack-plugin": "^2.0.1", "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "node-sass": "^4.11.0", "postcss-loader": "^3.0.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^4.30.0", "webpack-cli": "^3.3.0" } } 这样当我们修改js中的代码时,其会自动执行打包,我们只需要刷新一下页面就可以看到变化。 2.7.2使用webpack-dev-server 当我们使用watch时,我们虽然可以自动打包,但是页面不能自动刷新,需要我们手动刷新。 webpack-dev-server为我们提供了一个简单的web服务器,能够实时重新加载。 我们首先需要安装一下: npm install webpack-dev-server -D 接着我们需要修改webpack.config.js配置,告诉服务器,在哪里查找文件。 webpack.config.js: ...... module.exports = { mode:'development', devtool:'cheap-module-eval-source-map', //映射 entry:{ //配置入口文件 main:'./src/index.js', print:'./src/print.js' }, devServer:{ contentBase:'./dist', // 配置运行目录 open:true, // 自动打开浏览器 port:8080, // 服务器访问端口 proxy:{ // 配置跨越代理 'api':'http://loacalhost:3000' } }, ...... plugins:[ new HtmlWebpackPlugin({ template: 'src/index.html' //其会根据该html文件作模板自动导入打包后的js ,然 }), //后将生成的html放到出口文件夹中 new CleanWebpackPlugin() //配置删除的文件 ], output:{ //打包文件的出口 filename:'[name].js', //打包后的文件名 path:path.resolve(__dirname,'dist') //打包后文件存放的位置 } } 然后我们只需要在package.json中添加一个script脚本用于启动即可, "scripts": { "bundle": "webpack", "watch": "webpack --watch", "server": "webpack-dev-server" // 启动wepack-dev-server }, 运行npm webpack server 就可以看到webpack已经启动了一个服务器。 2.7.3使用webpack-dev-middleware webpack-dev-middleware是一个容器,它可以吧webpack处理后的文件传递给一个服务器。webpack-dev-server在内部就是使用了它。 这个仅仅了解就可以了,没必要自己写一个server,且性能没有webpack-dev-server好。这里主要是配合express server来使用 首先我们自己安装一下:npm install express webpack-dev-middleware --save-dev 然后我们需要在webpack.config.js中配置一下 output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), publicPath: '/' // } 接着我们需要创建一个server.js const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const app = express(); const config = require('./webpack.config.js'); const compiler = webpack(config); // Tell express to use the webpack-dev-middleware and use the webpack.config.js // configuration file as a base. app.use(webpackDevMiddleware(compiler, { publicPath: config.output.publicPath })); // Serve the files on port 3000. app.listen(3000, function () { console.log('Example app listening on port 3000!\n'); }); 这时我们只需要在package.json中添加一个srcipt脚本然后直接运行脚本就可以了 "server": "node server.js",
2.8模块热替换(HMR) 当我们启动server时,如果修改某个模块,它就会整个网页重新刷新,之前的操作又得重来。 HMR就是允许运行时更新各种模块,而不用完全刷新。也就是只更新有变动的模块,其他模板不动。 注:HMR只适合在开发环境下,并不适合在线上环境下配置。 因为HMR是webpack内置的插件,所以无需再下载,在使用之前,我们先将print.js删除。再引入HMR的相关配置。 webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); //导入插件 const webpack = require('webpack'); // 导入webpack module.exports = { mode:'development', devtool:'cheap-module-eval-source-map', //映射 entry:{ //配置入口文件 main:'./src/index.js' }, devServer:{ contentBase:'./dist', //配置运行目录 open:true, //自动打开浏览器 port:8080, //服务器访问端口 proxy:{ //配置跨越代理 'api':'http://loacalhost:3000' }, hot:true, // 开启热更新 hotOnly:true // 即使html错误,也不让页面更新, }, ...... plugins:[ new HtmlWebpackPlugin({ template: 'src/index.html' //其会根据该html文件作模板自动导入打包后的js ,然 }), //后将生成的html放到出口文件夹中 new CleanWebpackPlugin(), //配置删除的文件 new webpack.NamedModulesPlugin(), // 以便更容易查看要修补(patch)的依赖 new webpack.HotModuleReplacementPlugin() // 使用模块热替换插件 ], output:{ //打包文件的出口 filename:'[name].js', //打包后的文件名 path:path.resolve(__dirname,'dist') //打包后文件存放的位置 } } 到这里还没有完 要想实现热替换,还要配置一下index.js import header from './header' import content from './content' import footer from './footer' import './index.css' //引入css文件 header(); content(); footer(); //如果模块启用了HMR,就可以用 module.hot.accept(),监听模块的更新。 if (module.hot) { module.hot.accept('./header.js', function() { header() }) } 当我们单修改header.js时,其他模块并不会影响,只会更新header模块的内容。但我们会发现修改header会创建一个新的dom。 这是因为我们每次更新header模块都创建了一个dom 我们需要先修改一下js代码在更新前先清除之前添加的dom节点。 所以在使用HMR时要根据不同的代码做相应的设置。 HMR修改样式表 这个就更简单了,其实在上面我们应该实现了这个功能,无须做任何修改,当我们修改css样式时,其他模块是不会刷新的。无需设置module.hot.accept。 因为style-loader已经帮我们在后台使用 来源:http://www./content-4-169901.html |
|