webpack4.x开始官方文档是说要单独安装webpack-cli因此若是是用4.+就须要卸载clijavascript
//删除全局webpack-cli 卸载uninstall能够简写成un,全局-g的完整写法是--global npm uninstall -g webpack-cli //删除本地(局部)webpack-cli npm uninstall webpack-cli //删除全局webpack npm uninstall -g webpack //删除本地webpack npm un webpack
在空文件夹pratice下执行:css
//全局安装,默认最新版本,可使用@指定版本 npm install webpack -g //可使用cnpm淘宝镜像,这样速度更快 npm install -g cnpm --registry=https://registry.npm.taobao.org //用cnpm来安装。速度较快 cnpm install webpack -g //在3版本及以前安装webpack的时候就一同把webpack-cli也下载了,可是webpack4开始须要手动安装webpack-cli //安装webpack-cli cnpm install webpack-cli -g //查看版本号 webpack -v
至此webpack安装成功。html
而后执行webpack命令,结果报错:vue
缘由是webpack命令会默认打包该路径下src文件夹下的index.js文件,可是咱们这个pratice是个空文件夹。咱们能够新建一下src/index.js,而后再执行webpack就不会报错了。执行webpack后会自动生成一个dist文件夹,里面有将index.js打包好的main.js文件,默认是production压缩形式,能够执行webpack --mode development
打包成未压缩的格式,方便阅读。java
由于浏览器不支持require、import等那几个模块化变量,因此若是在src的js代码中使用了模块化,直接引用这样的js文件,浏览器是会报错的,因此须要经过webpack解析打包生成新的js文件,如上述的dist/main.js,而后引用这个js文件才ok。
node
新建sum.js:jquery
export default function(a,b){ return (a+b); }
新建minus.js:webpack
module.exports = function(a,b){ return a -b; }
项目pratice中新建demo.js:css3
import sum from './sum.js'/* 使用ES Module规范 */ var minus = require('./minus.js')/* 使用commonjs规范 */ var a = sum(1,5); console.log(a); var b = minus(100,9); console.log(b);
引入了ES Module和commonJS的模块化代码,浏览器是不支持的,因此要使用webpack来打包生成目标js文件,可是此次我不想被默认地打包到dist/main.js中,故可使用命令webpack demo.js -o ./hh/bundle.js
将模块化代码打包到指定的路径下。git
在此以前,我已经安装了全局的webpack,所以除了上述的项目路径下可使用webpack指令,其余的路径也是可使用的。可是最好再当前的项目路径下再安装一个本地局部的webpack。
先在项目根路径下执行npm init
指令进行项目初始化,该过程当中能够一路回车。会生成一个package.json文件,里面会记录你的各类加载器loader、插件、webpack或者jQuery等等相关信息。这是很是方便的一个事情,举个例子:好比我拿到了别人给个人一个项目,package.json里面就记录了该项目所依赖的各类东西,好比webpack是3.0版本的,而个人全局webpack是4.0版本的,那么不用慌,我能够查看package.json里记录的信息,手动安装一次本地的webpack3.0。更方便的是我能够直接执行npm install
指令自动下载package.json里面的全部依赖。
项目初始化完成后,执行npm install webpack --save-dev
安装本地开发版本的webpack。以后会生成一个node modules的文件夹,里面都是安装下来的包。webpack-cli也是同样本地安装一次。
webpack.config.js是webpack的默认配置文件,它的书写必须遵循CommonJs规范。它不是惟一的,一个项目能够有多个配置文件。
var path = require('path');/* path是node里面的变量 */ module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示当前路径文件夹 */ filename: 'my-first-webpack.bundle.js'/* 打包文件的名字 */ }, mode: 'development',/* 设置打包出来的文件是未压缩的格式 */ };
可使用对象的方式
var path = require('path');/* path是node里面的变量 */ module.exports = { entry: { index: './src/index.js', app: './src/app.js' }, output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示当前路径文件夹 */ filename: '[name].bundle.js' /* 注意这里用了[name] */ }, mode: 'development',/* 设置打包出来的文件是未压缩的格式 */ };
执行webpack
指令后会在dist文件夹下生成对应的app.bundle.js和index.bundle.js。注意filename: '[name].bundle.js'
loader用于处理非js文件。在webpack中写相应的配置规则,而且安装相应的插件。下面以less-loader为例:
//安装less-loader以前先安装less cnpm i style-loader less-loader css-loader less --save-dev
修改webpack.config.js:
var path = require('path');/* path是node里面的变量 */ module.exports = { entry: { index: './src/index.js', app: './src/app.js' }, output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示当前路径文件夹 */ filename: '[name].bundle.js' /* 注意这里用了[name] */ }, module: { rules: [ { test: /\.less$/, use: ['style-loader','css-loader','less-loader'] } ] }, mode: 'development',/* 设置打包出来的文件是未压缩的格式 */ };
如下是常见的几个loader:
module: { // 配置全部第三方loader 模块的 rules: [ // 第三方模块的匹配规则 { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }, { test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=7631&name=[hash:8]-[name].[ext]' }, // 处理图片路径的loader // limit给定的值,是图片的大小,单位是byte, 若是咱们引用的图片,大于或等于给定的limit值,则不会被转为base64格式的字符串, 若是图片小于给定的limit值,则会被转为base64的字符串 { test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }, //处理字体文件的 loader { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, //配置Babel来转换ES6语法 { test: /\.vue$/, use: 'vue-loader' } // 处理 .vue 文件的 loader ] },
loader被用于转换某些非js类型的模块,而插件则能够执行范围更广的任务,包括:从打包优化和压缩,一直到从新定义环境中的变量等等。
想要使用一个插件,先安装下载,而后须要require()它,而后把它添加到plugins数组中。
cnpm i html-webpack-plugin -D
执行该指令安装插件。
var path = require('path');/* path是node里面的变量 */ const HtmlWebpackPlugin = require('html-webpack-plugin'); //经过 npm 安装 module.exports = { entry: { index: './src/index.js', app: './src/app.js' }, output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示当前路径文件夹 */ filename: '[name].bundle.js' /* 注意这里用了[name] */ }, module: { rules: [ { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.ts$/, use: 'ts-loader' } ] }, plugins: [ new HtmlWebpackPlugin()/* 这里可传参也可不传参 */ ], mode: 'development',/* 设置打包出来的文件是未压缩的格式 */ };
而后会在dist文件中生成index.html文件,里面自动引入了咱们配置的js文件。
项目文件 treeShaking,先项目初始化npm init -y
,而后在生成的package.json里面添加"dev": "webpack --mode development"
,以后就不用每次都执行那么长的指令了,直接npm run dev
便可。同理也能够配置如下生产环境的指令"prod": "webpack --mode production"
js tree shaking一般用于描述移除 JavaScript 上下文中的未引用代码(dead-code),就是把未使用到的js代码去掉,精简js代码。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import
和 export
。
举个例子:
index.js:
import {abc} from '../sum.js'; abc();
sum.js
var abc = function(){ console.log('这是abc'); }; var heihei = function(){ console.log('这是heihei'); }; export { abc, heihei }
执行npm run dev后生成main.js,事实上咱们只引用到了sum.js中的abc,并无使用heihei,可是在webpack生成的main.js中搜索heihei关键字,发现依旧生成了关于heihei的代码,这形成了没必要要的冗余。
若是咱们执行npm run prod生成main.js,咱们发现没有生成关于heihei的代码,由于production模式下会自动去掉未使用的js代码,实现了tree shaking。
Loadsh
是一个一致性、模块化、高性能的 JavaScript 实用工具库。在数据操做时,咱们常常会用的 loadsh 封装好的一些工具方法,可是并不想把整个包打包进项目里面。要用到 tree-shaking,必然要保证引用的模块都是 ES6 规范的。lodash-es 是着具有 ES6 模块化的版本,只须要直接引入就能够。
先安装lodash-es: cnpm i lodash-es -D
sum.js中引入lodash-es库:
import lodash from 'lodash-es' var abc = function(){ console.log('这是abc'); }; var isArray = function(arg){ console.log(lodash.isArray(arg)); /* 判断传入的参数是否为数组 */ }; export { abc, isArray }
可是index.js中仍然只使用abc:
import {abc} from '../sum.js'; abc();
执行npm run dev,发现打包后的文件大小多达1.5Mb,相比于npm run prod生成的只有86Kb大小,由于它把lodash-es也打包进去了尽管咱们index.js中并无真正使用到lodash-es。可是尽管npm run prod生成的main.js确实比development的小,事实上它也打包了lodash-es里的代码了,只是比前者少了一点。由于npm run prod只能进行词法语法层面上的分析(sum.js中确实写了些关于lodash-es的代码),没法进行scope做用域上的分析,到底真正用没用它是不知道的。所以,npm run prod并非最佳的tree shaking。
这里 就须要用到一个插件: webpack-deep-scope-plugin。 先安装npm i webpack-deep-scope-plugin -D
在webpack.config.js中进行配置:
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default; module.exports = { plugins: [ new WebpackDeepScopeAnalysisPlugin() ] }
而后,执行npm run prod
,咱们发现打包出来的main.js大小不到1Kb。js tree shaking获得了较好的结果。
首先关于css的,就必定要先安装好style-loader和css-loader,以及写好配置规则。
而后须要安装一个插件mini-css-extract-plugin,用于单独抽离出css文件而非被写进main.js中。
写好配置规则:
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default; const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }, ] }, plugins: [ new WebpackDeepScopeAnalysisPlugin(), new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), ] }
注意的是,这里用MiniCssExtractPlugin.loader代替了以前的style-loader,style-loader是生成行间样式的,这个mini-css-extract-plugin插件能够生成一个main.css文件,而后咱们就可使用<link rel="stylesheet" href="../dist//main.css">
来引入css。在生成的dist/main.js中是没有关于demo.css的代码的,作到了单独抽离出css文件。
渡一视频这里用到了一个新的插件purifycss-webpack。可是这个插件已经被弃用了,用起来也有些bug,有些有被使用到的css没有被打包。我这里就不写了。
npm init -y npm i webpack -D npm i webpack-cli -D npm i css-loader -D npm i style-loader -D npm i less -D npm i less-loader -D npm i mini-css-extract-plugin -D
//demo.css *{ margin:0; padding:0; } .wrapper { position: relative; width: 500px; height: 500px; border: 1px solid #000; margin: 100px auto; transform: rotate(45deg); .box { position: absolute; width: 300px; height: 300px; border: 1px solid #000; top: 40%; left: 40%; text-align: center; a{ color: pink; font-size: 30px; line-height: 300px; } } }
//demo.less *{ margin:0; padding:0; } .wrapper { position: relative; width: 500px; height: 500px; border: 1px solid #000; margin: 100px auto; transform: rotate(45deg); .box { position: absolute; width: 300px; height: 300px; border: 1px solid #000; top: 40%; left: 40%; text-align: center; a{ color: pink; font-size: 30px; line-height: 300px; } } }
//index.js import './demo.css';
//index.html <body> <div class="wrapper"> <div class="box"> <a href="#">hello world</a> </div> </div> <script src="./dist/index.bundle.js"></script> </body>
var path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //单独抽离出css文件 module.exports = { entry:{ index: './index.js' }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js' }, module:{ rules:[ { test: /\.css$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', } ] }, { test: /\.less$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', }, { loader: 'less-loader', } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), ] }
有个问题就是关于css3的兼容代码,像demo.less里面的transform: rotate(45deg);
,须要写:
transform: rotate(45deg); -ms-transform: rotate(45deg); /* IE 9 */ -webkit-transform: rotate(45deg); /* Safari and Chrome */ -o-transform: rotate(45deg); /* Opera */ -moz-transform: rotate(45deg); /* Firefox */
基于postcss能够自动帮咱们加上这些兼容的前缀。
另外一个问题就是基于postcss能够解析cssnext的代码:
//带有cssnext代码的demo.less *{ margin:0; padding:0; } .wrapper { position: relative; width: 500px; height: 500px; border: 1px solid #000; margin: 100px auto; transform: rotate(45deg); .box { position: absolute; width: 300px; height: 300px; border: 1px solid #000; top: 40%; left: 40%; text-align: center; a{ color: pink; font-size: 30px; line-height: 300px; } } } :root{ --color: red; } a{ color: var(--color); }
cnpm i postcss postcss-loader autoprefixer cssnano postcss-cssnext -D
webpack.config.js
var path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry:{ index: './index.js' }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js' }, module:{ rules:[ { test: /\.css$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', } ] }, { test: /\.less$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', }, { /* 这部分要写在css-loader以后、less-loader以前 */ loader: 'postcss-loader', options:{ ident: 'postcss', plugins:[ require('postcss-cssnext')(), /* 用于解析cssnext代码,并实现autoprefixer的功能 */ // require('autoprefixer')(),/* 用于自动添加兼容前缀 , 注意在数组里面不能够有分号*/ require('cssnano')() /* 用于压缩代码 */ ] } }, { loader: 'less-loader', } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), ] }
npm install --save-dev html-webpack-plugin 安装之,而后再webpack.config.js中配置:
var HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), new HtmlWebpackPlugin({ filename: 'index.html', template: './index.html', minify:{ removeComments: true, /* 清理掉index.html里面的注释 */ collapseWhitespace: true, /* 清理掉空格 */ } }), ]
以后会生成dist/index.html,并且里面会自动引入所须要的css和 js,所以没必要再src/index.html中手动加入了。
事实上html-webpack-plugin还有不少能够配置的东西,网上都有。
另外咱们每次webpack要从新生成dist文件夹的时候,以前生成的一直都在着须要手动删除,比较麻烦,因此可使用一个插件clean-webpack-plugin
。 命令npm i clean-webpack-plugin -D
执安装之。
而后再webpack.config.js中配置:
const CleanWebpackPlugin = require('clean-webpack-plugin'); plugins: [ new CleanWebpackPlugin() ]
便可。
存在一个状况,假若有a,b两个js文件,它俩都引入了一个相同的第三方库或者是相同的自定义的模块。那么当咱们加载a文件的时候会把它打包出来,包括引用了的相同的库和模块,而加载b文件的时候也作了相同的事情,那么明明是相同的一个库或模块,却被打包了两次,这样就冗余了,下降了效率。咱们将同一个代码/模块/库打包了屡次,所以咱们须要作到提取公共JS代码,避免上述状况发生,实现减小代码冗余,提升项目的加载速度。
注意的是,提取公共代码针对的是多入口文件,若是只有一个js文件的话是不成立的。提取公共代码至少是两个文件。
//这是一个公共模块 module.js export default 'module'
//subPageA.js import './module.js'; export default 'subPageA';
//subPageB.js import './module.js'; export default 'subPageB';
//pageA.js import './subPageA.js'
//pageB.js import './subPageB.js'
entry:{ // index: './index.js' pageA: './src/pageA.js', pageB: './src/pageB.js', }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name][hash:5].bundle.js', chunkFilename: '[name][hash:5].js' }, optimization:{ splitChunks:{ cacheGroups:{ common:{ name:"common", /* 表示打包出来的公共模块的名字,不写的话就默认是chunkFilename */ chunks:'all', /* 表示做用的范围 */ minSize: 1, /* minSize表示生成公共代码块所须要最小的体积,默认是30kb */ minChunks: 2/* 表示公共模块被引用的最小次数,这里表示当一个模块被引用了2次以上就视为公共代码块 */ } } } },
打包后的:
先安装一下lodash: cnpm i lodash -D
//pageA.js import './subPageA.js'; import * as _ from 'lodash'; //引用lodash
//pageB.js import './subPageB.js'; import * as _ from 'lodash'; //引用lodash
注意的是,咱们以前打包出来的公共js文件是咱们自定义的公共模块,这里咱们引用了第三方库lodash,最好是不要和咱们自定义的模块一同被打包到一个公共js文件中,所以须要再配置一下。
entry:{ // index: './index.js' pageA: './src/pageA.js', pageB: './src/pageB.js', }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name][hash:5].bundle.js', chunkFilename: '[name][hash:5].js' }, optimization:{ splitChunks:{ cacheGroups:{ common:{ name:"common", /* 表示打包出来的公共模块的名字,不写的话就默认是chunkFilename */ chunks:'all', /* 表示做用的范围 */ minSize: 1, /* minSize表示生成公共代码块所须要最小的体积,默认是30kb */ minChunks: 2,/* 表示公共模块被引用的最小次数,这里表示当一个模块被引用了2次以上就视为公共代码块 */ priority: 1 /* 设置优先级,低于vendor的 */ }, vendor:{ name:'vendor', test:/[\\/]node_modules[\\/]/,/* 表示匹配node_modules里的第三方库lodash */ priority: 10,/* 设置优先级,先打包node_modules的*/ chunks: 'all' } } } }
打包以后:
项目根目录img
npm init -y npm i webpack -D npm i webpack-cli -D npm i css-loader -D npm i style-loader -D npm i mini-css-extract-plugin -D npm i clean-webpack-plugin -D npm i html-webpack-plugin -D //处理图片要安装如下loader和插件 cnpm i url-loader img-loader -D cnpm i file-loader -D npm install imagemin-pngquant -D npm i imagemin -D npm i html-loader -D
关于img-loader和imagemin-pngquant
var path = require("path"); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //单独抽离出css文件 var HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { index: './index.js' }, output: { path: path.resolve(__dirname, "dist"), filename: '[name][hash:5].bundle.js' }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader' } ] }, { // limit 给定的值,是图片的大小,单位是 byte, 若是咱们引用的 图片,大于或等于给定的 limit值,则不会被转为base64格式的字符串, 若是 图片小于给定的 limit 值,则会被转为 base64的字符串 test: /\.(jpg|png|gif|bmp|jpeg)$/, use: [ { loader: 'url-loader', options: { name: '[name][hash:5].[ext]', /* 限制图片大小 */ limit: 77967, outputPath: 'img' /* 会建立dist/img文件夹 */ } }, // use: 'url-loader?limit=7631&name=[hash:8]-[name].[ext]' { loader:'img-loader', options:{ plugins:[ require('imagemin-pngquant')({ quality:[0.3,0.6] }), ] } } ] }, { test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), new HtmlWebpackPlugin({ filename: 'index.html', template: './index.html', // minify:{ // removeComments: true, /* 清理掉index.html里面的注释 */ // collapseWhitespace: true, /* 清理掉空格 */ // } }), new CleanWebpackPlugin() ] }
以上到这一步为止,只能处理css中的url,尚没法解析html中引用的图片路径,好比html中的<img src="./logo.png" alt="">
是会报错的。所以须要安装一个html-loader,而后在webpack.config.js中的module->rules添加一个test:
{ test:/\.html$/, use: [ { loader: 'html-loader', options:{ attrs:['img:src'] } } ] }
而后就 ok 了
先安装 : npm install webpack-dev-server -g 要全局安装
安装成功后执行命令webpack-dev-server --open
就能够了,默认在localhost:8080端口,项目就启动了。可是要注意的是,该命令并无把项目进行打包,因此最后发布的时候仍是要用webpack指令进行打包。
它会实时监听代码的变化。
module.exports = { ... devServer:{ port:'9091', // contentBase:'dist' /* 不填的话默认是项目根路径 */ } }
mock一个假数据
//data.json { "name": "新裤子乐队", "birthday": "1996" }
//index.js var data = require('./data.json'); console.log(data);
而后就能够了。
再来讲说使用ajax的:
//index.js import $ from 'jquery'; var data = require('./data.json'); console.log(data); $.ajax({ url: 'http://localhost:9091/data.json', success: function(data){ console.log('ajax获取数据成功:'); console.log(data); }, error:function(){ console.log('获取数据失败!'); } })
虽说webpack-dev-server能够自动监听代码变化并自动刷新,可是它每次刷新都要整个页面刷新,整个页面的全部数据都要从新请求一遍,实际上修改的只是一部分,其余的是没必要要从新请求的。这里引出了热更新这个概念。热更新,英文为 hot module replace,直译为“热模块替换”,这里的热模块就是被修改的那部分模块。
webpack中内置了热更新的插件,因此没必要再下载,直接再webpack.config.js中配置:
... var Webpack = require('webpack');//第一步 module.exports = { ... plugins:[ ..., //第二步 new Webpack.HotModuleReplacementPlugin(), /* webpack内置的热更新插件 */ ], devServer:{ port: "9091", // contentBase: 'dist' /* 默认是项目根路径 */ hot: true, /* 开启热更新 */ } }
这样热更新基本实现了。
【须要注意的是】:
对于CSS,若是使用了MiniCssExtractPlugin单独抽离出来css文件,那么这个热更新是无效的。之对于使用style-loader的有效。
对于JS,目前这一步是没法实现对于js的热更新的 ,须要再js文件中添加以下代码:
if(module.hot){ module.hot.accept(); }
这样就实现了对于js代码的热更新。