构建就是把源代码转换成发布到线上的可执行JavaScrip、CSS、HTML代码,包括如下内容:
代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。首先确保电脑已经安装了nodejs,推荐采用nvm的形式进行安装,这样就不用配置环境变量或者创建软链接。
mkdir webpack-learn //通过命令行创建文件夹 cd webpack-learn //打开创建的文件夹 npm init -y //初始化一个项目 npm install webpack webpack-cli -D //本地安装webpack和webpack-cli mkdir src //创建src目录来存放源代码 mkdir dist //创建dist目录来存放打包后的代码 复制代码在src目录下创建index.js
let str = require('./a'); console.log(str); 复制代码在src目录下创建a.js
module.exports='webpack-learn' 复制代码在dist目录下创建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>Document</title> </head> <body> <div id="root"></div> <script src="bundle.js"></script> </body> </html> 复制代码利用webpack进行打包,关于commonjs不清楚的,请参考require()源码解读
npx webpack -- mode development //以默认以开发模式进行打包 复制代码<figure>[图片上传中...(image-d122a4-1540439603940-4)]
<figcaption></figcaption>
</figure>
从上面的代码中可以看到文件打包到了dist/main.js中,所以需要进行相应的配置webpack.config.js来自定义结果文件名。
现在页面是手动创建到dist目录下的,一个页面还好,如果存在多个页面,手动创造html的代价是很大的,可以利用html-webpack-plugin来自动创建页面:
npm install html-webpack-plugin -D 复制代码 //src/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>Document</title> </head> <body> </body> </html> 复制代码 const path = require('path'); const webpack =require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry:{ pageA:'./src/a', pageB:'./src/b' }, output: { path: path.resolve(__dirname,'dist'), filename:'[name].[hash:8].js' }, module: {}, plugins: [ //在实际项目中,通过读取需要创建的页面信息,遍历创建实例 new HtmlWebpackPlugin({ template:'./src/index.html', filename:'pageA.html', chunks:['pageA,pageB'], //数组,可以放多个chunk //页面资源携带哈希值exp:pageA.fa112c62?r2452567&4124 //中间哈希一直都有,这个后面的哈希只在页面引用添加在页面中 hash:true, minify:{ collapseWhitespace:true, //压缩代码,去除空格和换行 removeAttributeQuotes:true//压缩代码,去除属性双引号 } }), new HtmlWebpackPlugin({ template:'./src/index.html', filename:'pageB.html', chunks:['pageB'], hash:true, minify:{ collapseWhitespace:true, removeAttributeQuotes:true } }) ], devServer: {}, resolve:{}, } 复制代码由于每次打包都需要执行npx webpack --mode development,所以可以在package.json中进行配置:
"scripts": { //等价于webpack --config webpack.config.js --mode development //默认是执行webpack.config.js,可以根据env来配置执行不同的文件 "start": "webpack --mode development" }, 复制代码首先必须的安装插件webpack-dev-derver:
npm install webpack-dev-server -D 复制代码修改script:
"scripts": { "dev":"webpack-dev-server --mode development" "build": "webpack --mode development" }, 复制代码修改配置文件:
//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>Document</title> </head> <body> <div id='app'></div> </body> </html> 复制代码 //a.js module.exports = 'hello' 复制代码 //index.js let str = require('./a'); document.getElementById('app').innerHTML = str; 复制代码 const path=require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports={ entry: './src/index.js', output: { path: path.resolve(__dirname,'dist'), filename:'bundle.js' }, module: {}, plugins: [ new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', hash:true, minify:{ collapseWhitespace:true, removeAttributeQuotes:true } }) ], devServer: { //devServer会在打包的时候把生成的文件放在内存中, //并且是以根目录为参照目录而不是dist目录,所以需要修改 contentBase:'./dist', port:'3000', } resolve:{}, } 复制代码通过浏览器,输入localhost:3000就可以看到
<figure>[图片上传中...(image-18105c-1540439603940-3)]
<figcaption></figcaption>
</figure>
通过修改a.js中module.exports = 'hello1',可以看到
<figure>[图片上传中...(image-f88b0-1540439603940-2)]
<figcaption></figcaption>
</figure>
这样会有一个问题,就是只要源代码中存在改动,就会刷新页面。假如在react本地开发的时候,有很多组件,其中一个组件的代码修改了,不希望所有的组件都更新,可以利用热更新来解决:
const path = require('path'); const webpack =require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports={ entry:'./src/index.js', output: { filename:'bundle.js', path: path.resolve(__dirname,'dist') }, module: {}, plugins: [ //使用热更新插件 new webpack.HotModuleReplacementPlugin(); new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', hash:true, minify:{ collapseWhitespace:true, removeAttributeQuotes:true } }) ], devServer: { contentBase:'./dist', port:'3000', hot:true //热更新开关,使用websocket来通知页面更新 }, resolve:{}, } 复制代码 //index.js let str = require('./a'); document.getElementById('app').innerHTML = str; //这里必须加这段,不然的话,还是没有办法使用热更新 if(module.hot){ module.hot.accept(); } 复制代码webpack每找到一个Module,就会根据配置的Loader去找出对应的转换规则,让js能过编译css、ejs、jsx和图片的各种格式。
执行npm run build之后发现出现以下报错,说明需要配置loader:
<figure>[图片上传中...(image-f45890-1540439603939-1)]
<figcaption></figcaption>
</figure>
首先需要安装loader
npm install css-loader style-loader -D 复制代码 const path = require('path'); const webpack =require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry:{ index:'./src/index' }, output: { filename:'[name].[hash:8].js', path: path.resolve(__dirname,'dist') }, module: { rules:[ { test:/\.css$/, //多个loaders用数组,loader的形式有字符串和对象两种 //字符串形式:'xxx?option1!yyyy' //对象形式{loader:'xxx',options:{option1:yyyy}} //less-laoder将less转化为css,css-loader解析css,style-loader插入到style标签中 use:['style-oader','css-loader'] } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', chunks:['index'] }) ], devServer: { contentBase:'./dist', port:'3000', hot:true }, resolve:{}, } 复制代码用npm run dev启动服务可以看到效果,但是这样有一个问题,css样式采用的内嵌式,最好能抽离出来使用外链式引入,可以使用插件mini-css-extract-plugin:
npm install mini-css-extract-plugin -D 复制代码 const path = require('path'); const webpack =require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry:{ index:'./src/index' }, output: { filename:'[name].[hash:8].js', path: path.resolve(__dirname,'dist') }, module: { rules:[ { test:/\.css$/, use:[{ loader:MiniCssExtractPlugin.loader, options:{ //将css中的路经前面添加,background:url('xxxx') //http://ssss/xxxxx publicPath:'http://sssss' } }, 'css-loader' ] } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new MiniCssExtractPlugin({ filename:index.css, }), new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', chunks:['index'] }) ], devServer: { contentBase:'./dist', port:'3000', hot:true }, resolve:{}, } 复制代码 postcss-loader和autoprefixer插件由于前端写的代码要兼容各种浏览器,css属性为了兼容各大浏览器,往往需要添加各种前缀,但是同一个属性写多份,这个工作量还是比较大的,有一个postcss-loader可以配合autoprefixer插件使用
//index.css body{ background:red; transform: rotate(0,0,100deg); } 复制代码 npm install postcss-loader autoprefixer -D 复制代码 //创建postcss.config.js module.exports ={ plugins:[ require('autoprefixer') ] } 复制代码 const path = require('path'); const webpack =require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry:{ index:'./src/index' }, output: { filename:'[name].[hash:8].js', path: path.resolve(__dirname,'dist') }, module: { rules:[ { test:/\.css$/, use:[ {loader:MiniCssExtractPlugin.loader}, 'css-loader', 'postcss-loader' //添加css前缀处理laoder ] } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new MiniCssExtractPlugin({ filename:index.css, }), new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', chunks:['index'] }) ], devServer: { contentBase:'./dist', port:'3000', hot:true }, resolve:{}, } 复制代码项目中引入图片的方式有三种:
通过js引入 //index.js import './index.css' import jpg from './1.jpg' let img = new Image(); img.src=jpg; document.body.appendChild(img); if(module.hot){ module.hot.accept(); } 复制代码 通过css引入 //index.css body{ background: url('./1.jpg') no-repeat right; } 复制代码 通过img的src属性引入 //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>Document</title> </head> <body> <img src="./1.jpg" alt=""> </body> </html> 复制代码处理前两种引入图片的方式需要使用file-loader和url-loader,其中url-laoder内部会引用file-loader,它们的作用就是解析js和css中的图片链接然后将图片变成base64。后一种引入图片的方式需要使用html-withimg-loader。
npm install file-loader url-loader html-withimg-loader -D 复制代码 const path = require('path'); const webpack =require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry:{ index:'./src/index' }, output: { filename:'[name].[hash:8].js', path: path.resolve(__dirname,'dist') }, module: { rules:[ { test:/\.css$/, use:[{ loader:MiniCssExtractPlugin.loader, },'css-loader'] }, { test:/\.jpg|png/, use:{ loader:'url-loader', options:{ //大于8k的图片会打包到dist目录下,小于8k的图片会生成base64插入到引用的地方 //base64会使资源变大1/4,但是base64无需发送请求,资源比较小时使用base64最佳 limit:8*1024 } } }, { test:/\.html$/, use:'html-withimg-loader' } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new MiniCssExtractPlugin({ filename:index.css, }), new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', chunks:['index'] }) ], devServer: { contentBase:'./dist', port:'3000', hot:true }, resolve:{}, } 复制代码<figure>[图片上传中...(image-c44f47-1540439603939-0)]
<figcaption></figcaption>
</figure>
由于ES6的简洁和API扩展,有很多开发者使用ES6进行开发,但是由于浏览器的品牌和版本的不同,就出现了开发时使用ES6然后同意转化成ES5的情况。这时候就需要使用babel对ES6+的语法进行转译,关于babel的各种配置可以参考对babel-transform-runtime,babel-polyfill的一些理解。
npm install babel-core babel-loader babel-preset-env babel-preset-stage-0 babel-plugin-tranform-runtime -D 复制代码 //创建.babelrc文件 //preset中包含了一组用来转换ES6+的语法的插件,但是还不转换新的API //如需使用新的API,例如set(),还需要使用对应的转换插件或者polyfill(填充库) { presets:{ 'env', //环境变量,根据不同浏览器环境而对应的转码 'stage-0' //转译ES6+(0 > 1 > 2 > 3 > 4) } plugins:{ 'tranform-runtime' } } 复制代码 const path = require('path'); const webpack =require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry:{ index:'./src/index' }, output: { filename:'[name].[hash:8].js', path: path.resolve(__dirname,'dist') }, module: { rules:[ { test:/\.css$/, use:[{ loader:MiniCssExtractPlugin.loader, },'css-loader'] }, { test:/\.jpg|png/, use:{ loader:'url-loader', options:{ limit:8*1024 } } }, { test:/\.html$/, use:'html-withimg-loader' }, { //使用babel-loader test:/\.js$/, use:'babel-loader', exclude:/node_modules/ //排除编译ndoe——modules } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new MiniCssExtractPlugin({ filename:index.css, }), new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', chunks:['index'] }) ], devServer: { contentBase:'./dist', port:'3000', hot:true }, resolve:{}, } 复制代码以jquery为例来说说webpack引用第三方插件的几种方式:
npm install jquery 复制代码plugin和loader的区别在于loader只在编译module时执行,而plugin可能在webapck工作流程的各个阶段执行。
清除dist目录
module.exports = { mode:'production', 生产环境会自动压缩 entry:{ index:'./src/index' }, output: { filename:'[name].[hash:8].js', path: path.resolve(__dirname,'dist') }, module: { rules:[ { test:/\.css$/, use:[{ loader:MiniCssExtractPlugin.loader, },'css-loader'] }, { test:/\.jpg|png/, use:{ loader:'url-loader', options:{ limit:8*1024 } } }, { test:/\.html$/, use:'html-withimg-loader' }, { test:/\.js$/, use:'babel-loader', exclude:'/node_modules/' } ] }, plugins: [ new CleanWebpackPlugin(['dist/*.*']), //清空dist目录 new webpack.HotModuleReplacementPlugin(), new MiniCssExtractPlugin({ filename:index.css, }), new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html', chunks:['index'] }) ], externals:{ 'jquery':'$' } devServer: { contentBase:'./dist', port:'3000', hot:true }, resolve:{}, } 复制代码学习webpack的过程是从官方文档开始学习的,照着敲了一遍,然后搜索相关的内容。
作者:梦想攻城狮 链接:https://juejin.im/post/5bbec0395188255c305012d1 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。