本文介绍webpack3.x的使用

目录

开始

css文件打包

image文件打包

字体文件打包

json文件打包

csv文件和xml文件打包

多入口文件打包

清理dist目录

development开发环境错误定位

development开发环境开发模式

development开发环境模块热替换

development开发环境模块热替换存在的问题

Tree Shaking(死代码终结者)

development和production代码分离与合并

系统环境变量设置

css文件剥离

代码分割

按需加载

浏览器资源缓存

全局导入

no export导出

静态服务器搭建


  1. 安装webpack
    npm init -y
    npm install webpack --save-dev
    使用命令行bundle文件
    npx webpack src/index.js dist/bundle.js
    使用配置文件bundle文件
    项目根目录创建webpack.config.js文件,写入如下内容
    const path = require("path");
    module.exports = {
    entry: './src/index.js',
    output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
    }
    }
    运行命令 npx webpack --config webpack.config.js
    或者在package.json中写
    "scripts": {
    "build": "webpack"
    }
    运行 npm run build 即可,默认去找项目根目录的webpack.config.js
  2. 项目根目录下面一个dist目录用来放打包后的文件,src目录用来放源文件,index.html放在dist目录中,webpack默认这样的
    在src/index.js中 import './style.css,让webpack解析此类文件,完成如下几步
    首先,安装插件
    npm install --save-dev style-loader css-loader
    其次,修改webpack.config.js的配置为
    const path = require("path");
    module.exports = {
    entry: './src/index.js',
    output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, "dist")
    },
    module: {
    rules: [
    {
    test: /\.css$/,
    use: [
    'style-loader',
    'css-loader'
    ]
    }
    ]
    }
    }
    最后,运行webpack即可,css文件都打包到bundle.js中,运行时自动插入到header中
    提示:
    css-loader对css文件中的url图片都做了处理,将其替换成打包后的绝对路径
  3. 在src/index.js中 import MyImage from './4.jpg'; 其中MyImage是图片的打包后的绝对路径
    安装插件,npm install --save-dev file-loader
    修改,webpack.config.js
    在module中添加如下rules
    {
    test: /\.(png|svg|jpg|gif)$/,
    use: [
    'file-loader'
    ]
    }
    运行打包即可
  4. 在css文件中写自定义字体,如
    @font-face {
    font-family: 'MyFont';
    src: url('./my-font.woff2') format('woff2'),
    url('./my-font.woff') format('woff');
    font-weight: 600;
    font-style: normal;
    }
    同样使用file-loader,修改,webpack.config.js
    在module中添加如下rules
    {
    test: /\.(woff|woff2|eot|ttf|otf)$/,
    use: [
    'file-loader'
    ]
    }
    运行webpack即可
  5. webpack默认支持json文件导入,所以无需任何额外操作
    在src/index.js中
    import data from './index.json';
    console.log(data);
    index.json中这样定义
    {
    "a": 1,
    "b": 2
    }
  6. 安装插件 npm install --save-dev csv-loader xml-loader
    添加如下的rules
    {
    test: /\.(csv|tsv)$/,
    use: [
    'csv-loader'
    ]
    },
    {
    test: /\.xml$/,
    use: [
    'xml-loader'
    ]
    }
    使用方式和json文件类似
  7. 如果你想把你的js文件拆分成小的文件组合,多入口很好的解决这个问题
    比如,在src/index.js文件中 import printMe from './print.js';
    修改 webpack.config.js 配置如下
    const path = require("path");
    module.exports = {
    entry: {
    app: './src/index.js',
    print: './src/print.js'
    },
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, "dist")
    }
    }
    修改 dist/index.html
    <script src="./print.bundle.js"></script>
    <script src="./app.bundle.js"></script>
    打包编译即可
    问题:我们必须手动在index.html中插入js文件,可以使用html-webpack-plugin自动插入脚本
    安装,npm install --save-dev html-webpack-plugin
    修改 webpack.config.js 文件如下
    const path = require("path");
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
    entry: {
    app: './src/index.js',
    print: './src/print.js'
    },
    plugins: [
    new HtmlWebpackPlugin({
    title: "自动生成的html文件"
    })
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, "dist")
    }
    }
    运行编译即可
  8. 在打包之前,先清理干净我们的dist输出目录,是非常好的做法
    安装 npm install clean-webpack-plugin --save-dev
    修改 webpack.config.js 文件
    const path = require("path");
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    module.exports = {
    entry: {
    app: './src/index.js',
    print: './src/print.js'
    },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "自动生成的html文件"
    })
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, "dist")
    }
    }
    运行编译即可
  9. 定位错误信息
    所有的文件打包到bundle中,如果代码是错误的,要想定位到错误的源文件,必须使用source-map
    修改 webpack.config.js
    添加 devtool: 'inline-source-map',
    运行打包,如果在浏览器中运行错误,会定位错误源
  10. 每次查看代码都要运行 npm run build, 然后刷新浏览器,这是很烦的一个操作
    webpack有如下几种模式,自动的帮你完成上面两个操作
    Watch 模式
    首先,在package.json中添加如下命令
    "watch": "webpack --watch"
    然后运行 npm run watch
    当你修改你的代码,webpack自动帮你编译
    webpack-dev-server 模式
    首先,安装 npm install --save-dev webpack-dev-server
    然后,修改 webpack.config.js 在module.exports中添加如下属性
    devServer: {
    contentBase: './dist'
    }, // 作用是告诉webpack在localhost:8080上观察dist目录下面的内容
    然后,在package.json中添加如下命令
    "start": "webpack-dev-server --open"
    运行 npm start
    当你修改了你的代码,webpack会自动帮你编译,并且自动帮你刷新浏览器
    webpack-dev-middleware 模式
    这种模式和webpack-dev-server类似,不过提供了更多的配置
    安装 npm install --save-dev express webpack-dev-middleware
    修改 webpack.config.js 为如下内容
    const path = require("path");
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    module.exports = {
    entry: {
    app: './src/index.js',
    print: './src/print.js'
    },
    devtool: 'inline-source-map',
    // devServer: {
    // contentBase: './dist'
    // },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "自动生成的html文件"
    })
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, "dist"),
    publicPath: '/'
    }
    }
    在项目根目录下添加 server.js,写如下内容,涉及到node.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);
    app.use(webpackDevMiddleware(compiler, {
    publicPath: config.output.publicPath
    }))
    app.listen(3000, function() {
    console.log("app listenging on port 3000!\n");
    })
    在package.json中添加如下命令
    "server": "node server.js"
    运行 npm run server 即可
    打开 localhost:3000 查看
  11. 热替换,简单理解,就是什么文件修改了,更新什么文件
    开启模块热替换webpack方式
    修改webpack.config.js文件为
    const path = require("path");
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const webpack = require('webpack');
    module.exports = {
    entry: {
    // app: './src/index.js',
    // print: './src/print.js'
    app: './src/index.js' // 多bundle文件不需要写多个entry实现,HotModuleReplacementPlugin自动完成
    },
    devtool: 'inline-source-map',
    devServer: {
    contentBase: './dist',
    hot: true // 开启热替换
    },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "自动生成的html文件"
    }),
    new webpack.NamedModulesPlugin(), // 在重新编译的时候,会展示被更新模块的相对路径,如 “ [./src/print.js] ./src/print.js 98 bytes {0} [built] ”
    new webpack.HotModuleReplacementPlugin()
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, "dist")
    }
    }
    运行 npm start
    开启模块热替换node.js方式
    首先,修改webpack.config.js文件
    去掉devServer配置项
    const path = require("path");
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const webpack = require('webpack');
    module.exports = {
    entry: {
    app: './src/index.js'
    },
    devtool: 'inline-source-map',
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "自动生成的html文件"
    }),
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, "dist")
    }
    }
    修改server.js
    const webpack = require('webpack');
    const webpackDevServer = require('webpack-dev-server');
    const config = require('./webpack.config.js');
    const options = {
    contentBase: './dist',
    hot: true,
    host: 'localhost'
    };
    webpackDevServer.addDevServerEntrypoints(config, options);
    const compiler = webpack(config);
    const server = new webpackDevServer(compiler, options);
    server.listen(5000, 'localhost', () => {
    console.log("dev server listening on port 5000");
    })
    运行 npm run server
    css文件的热替换,直接使用style-loader即可自动完成
  12. src/index.js内容如下
    import printMe from './print.js';
    function fn() {
    console.log("aaaa")
    var element = document.createElement('div');
    var btn = document.createElement('button');
    element.innerHTML = "hahaha";
    btn.innerHTML = "按钮";
    btn.onclick = printMe;
    element.appendChild(btn);
    return element;
    }
    document.body.appendChild(fn());
    if(module.hot) { // 监听模块热更新
    module.hot.accept('./print.js', function(){
    console.log("print.js更新了");
    printMe();
    })
    }
    src/print.js内容如下
    export default function printMe() {
    console.log("print.js")
    }
    当更新print.js时,点击按钮,输出的信息还是之前的信息,这可以说是webpack热替换的一个硬伤
    解决上面这个问题,最原始的方法,如下
    if(module.hot) {
    module.hot.accept('./print.js', function(){
    document.body.removeChild(element);
    element = fn();
    document.body.appendChild(fn);
    })
    }
  13. 当你的代码中只import某个文件的一部分,但是打包的时候这个文件全部的内容都包含进来了,这就是Tree Shaking要解决的
    下载插件 npm install --save-dev uglifyjs-webpack-plugin
    将插件添加到webpack.config.js中即可
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
    plugins: [
    new UglifyJSPlugin()
    ]
    运行npm run build,只用import了的代码才会被打包
    当一个文件的内容被其他的文件导入,然后重新导出,此时Tree Shaking失效了
    注:在命令行中 --optimize-minimize 可以实现同样效果
  14. 将webpack的配置拆分成development和production需要用到webpack-merge插件,作用是合并配置
    安装插件 npm install --save-dev webpack-merge
    将webpack.config.js文件删除,添加如下三个
    webpack.common.js
    const path = require('path');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
    entry: {
    app: './src/index.js'
    },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "生成的index.html"
    })
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
    }
    }
    webpack.dev.js
    const merge = require('webpack-merge');
    const common = require('./webpack.common.js');
    module.exports = merge(common, {
    devtool: 'inline-source-map',
    devServer: {
    contentBase: './dist'
    }
    });
    webpack.prod.js
    const merge = require('webpack-merge');
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
    const common = require('./webpack.common.js');
    module.exports = merge(common, {
    devtool: 'source-map', // 生产环境最好也加上source-map,如果你不需要可以去掉
    plugins: [
    new UglifyJSPlugin({
    sourceMap: true
    })
    ],
    output: {
    publicPath: '/Test' // 指定发布路径
    }
    });
    修改package.json命令
    "scripts": {
    "start": "webpack-dev-server --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
    }
  15. 利用process.env.NODE_ENV可以方便的设置development和production模式
    利用webpack的DefinePlugin插件,可以方便设置此变量
    修改webpack.prod.js文件
    const merge = require('webpack-merge');
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
    const common = require('./webpack.common.js');
    const webpack = require('webpack');
    module.exports = merge(common, {
    devtool: 'source-map',
    plugins: [
    new UglifyJSPlugin({
    sourceMap: true
    }),
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
    })
    ]
    });
    然后在你自己的源代码中,随便访问环境变量
    if(process.env.NODE_ENV != 'production') {
    console.log("我不是production模式");
    }else {
    console.log("我是production模式")
    }
    注:在命令行中 --define process.env.NODE_ENV="'production'" 可以实现同样效果
    利用webpack自己的内置的方式设置环境变量
    使用--env自定义环境变量
    如,package.json命令 "start": "webpack-dev-server --open --config webpack.dev.js --env.production=false --env.MyEnv=development --progress",
    修改webpack.dev.js
    module.exports = env => {
    console.log(env); -> 输出 { production: 'false', MyEnv: 'development' }
    return merge(common, {
    devtool: 'inline-source-map',
    devServer: {
    contentBase: './dist'
    }
    });
    }
  16. 将css从bundle中剥离出来,需要如下几步
    安装 npm install --save-dev extract-text-webpack-plugin
    修改 webpack.common.js文件
    const path = require('path');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ExtractTextPlugin = require("extract-text-webpack-plugin");
    module.exports = {
    entry: {
    app: './src/index.js'
    },
    module: {
    rules: [
    {
    test: /\.css$/,
    use: ExtractTextPlugin.extract({ // 剥离文件
    fallback: "style-loader",
    use: "css-loader"
    }),
    include: path.resolve(__dirname, "src")
    }
    ]
    },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "生成的index.html"
    }),
    new ExtractTextPlugin({ // 设置插件选项
    filename: "styles.css",
    disable: process.env.NODE_ENV == "developement"?true: false
    })
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
    }
    }
    只在production模式中才会剥离css,在development中不剥离从而提高性能
  17. 代码分割有如下几种方式
    多入口,开篇讲过写法
    entry: {
    app: './src/index.js',
    entry: './src/print.js'
    },
    这种写法存在问题,当这些entry中都import了相同的模块,那么打包后的每个bundle中都存在重复的代码
    解决办法如下
    在webpack.common.js中的plugins项中,添加如下配置
    new webpack.optimize.CommonsChunkPlugin({
    name: 'common' // 剥离的文件名
    })
    CommonsChunkPlugin的作用就是将bundle中的重复代码提取到单独的文件中
    动态import,单入口
    也就是 import() 语法,此语法返回一个promise对象,使用步骤如下
    首先,修改webpack.common.js配置如下
    const path = require('path');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ExtractTextPlugin = require("extract-text-webpack-plugin");
    module.exports = {
    entry: {
    app: './src/index.js'
    },
    module: {
    rules: [
    {
    test: /\.css$/,
    use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
    })
    }
    ]
    },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "生成的index.html"
    }),
    new ExtractTextPlugin({
    filename: "styles.css",
    disable: process.env.NODE_ENV == "developement"?true: false
    })
    ],
    output: {
    filename: '[name].bundle.js',
    chunkFilename: '[name].bundle.js', // import() 分离多文件的关键配置
    path: path.resolve(__dirname, 'dist')
    }
    }
    然后,假设src/other.js中的代码如下
    export function fn () {
    console.log("相互引用的js");
    }
    假设src/print.js中的代码如下
    export async function GetOtherComponent() {
    const _ = await import(/* webpackChunkName: "myOther" */ './other.js');
    return _;
    }
    假设src/index.js中的代码如下
    import { GetOtherComponent } from './print.js';
    GetOtherComponent().then(data => {
    data.fn();
    console.log(data);
    })
    运行 npm run build, 输出了myOther.bundle.js文件
    代码分离到此结束
  18. 按需加载还是需要借助import()函数
    button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
    var print = module.default; // 表示print.js中的export.default的对象
    print();
    });
  19. 利用webpack的chunkhash可以方便的监测到文件是否修改,如果修改自动更改文件名,浏览器更新资源
    使用相当简单,只需要将output改为
    output: {
    // filename: '[name].bundle.js',
    filename: '[name].[chunkhash].js',
    chunkFilename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
    }
    配合CommonsChunkPlugin可以将公共的不变的代码剥离出来,从而让浏览器永远缓存不需要重新下载
    修改webpack.common.js配置文件如下
    const path = require('path');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ExtractTextPlugin = require("extract-text-webpack-plugin");
    const webpack = require('webpack');
    module.exports = {
    entry: {
    app: './src/index.js',
    vendor: [ // 设置需要剥离的第三方库
    "moment"
    ]
    },
    module: {
    rules: [
    {
    test: /\.css$/,
    use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
    })
    }
    ]
    },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "生成的index.html"
    }),
    new ExtractTextPlugin({
    filename: "styles.css",
    disable: process.env.NODE_ENV == "developement"?true: false
    }),
    new webpack.optimize.CommonsChunkPlugin({
    name: "vendor" // 将vendor入口打包单独输出
    }),
    new webpack.optimize.CommonsChunkPlugin({
    name: "manifest" // 打包剩余部分,必须写在最后
    })
    ],
    output: {
    filename: '[name].[chunkhash].js',
    path: path.resolve(__dirname, 'dist')
    }
    }
    存在的问题
    webpack打包其中有一个module.id,这个东西是用来标识模块的,从而在剥离的manifest文件中引用
    默认情况下module.id是根据模块打包的顺序来自增编号的,当用户打乱了模块import的顺序,那么打
    包后所有的文件都被修改了,这是模块的chunkhash都发生改变了,为了解决这个问题,请使用如下方式
    只需要将你的webpack.common.js中的plugins修改为
    plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
    title: "生成的index.html"
    }),
    new ExtractTextPlugin({
    filename: "styles.css",
    disable: process.env.NODE_ENV == "developement"?true: false
    }),
    new webpack.HashedModuleIdsPlugin(), // 添加这一句话就ok了
    new webpack.optimize.CommonsChunkPlugin({
    name: "vendor"
    }),
    new webpack.optimize.CommonsChunkPlugin({
    name: "manifest"
    })
    ],
  20. 当你需要导入jQuery这样带有全局属性的模块的时候,此部分是你必须掌握的
    首先,将jquery下载到项目,修改webpack.common.js文件,在plugins配置项中添加如下
    new webpack.ProvidePlugin({
    $: 'jquery',
    jquery: 'jquery'
    }), 当程序中出现$或者jquery时,webpack在编译的时候就会去找对应的模块
    然后,在程序中直接访问即可,如 console.log($);
    当然,当你的程序中只用到了其他插件的一部分代码,你想利用tree shaking的技术,可以参考如下
    new webpack.ProvidePlugin({
    join: ['lodash', 'join'] // 表示将lodash中的join方法注册成全局的,webpack只会打包此单个方法
    })
  21. 当你的代码中不export任何代码,如何让其他模块import?,请看下面
    首先在 other.js 中定义如下
    var obj = {
    "a": 1,
    "b": 2,
    "c": 3
    }
    var a = 1;
    var fn = function() {
    console.log(111);
    }
    在 index.js 中导入
    import { a,fn,c } from './other.js';
    安装插件 npm i exports-loader --save
    修改webpack.common.js文件,在module中添加如下rules
    {
    test: require.resolve('./src/other.js'),
    use: 'exports-loader?a,fn,c=obj.c'
    }
  22. 当我们的代码编译完成之后,如何搭建一个静态服务器来查看我们编译的代码呢?
    安装 npm install http-server --save-dev
    在package.json文件中添加命令
    "server": "http-server dist"
    编译代码 npm run build 然后 npm run server 即可
    存在的问题,当我们将server停止后,页面将无法访问,解决办法如下
    安装 npm install workbox-webpack-plugin --save-dev
    将插件配置,写到webpack.config.js中
    const WorkboxPlugin = require('workbox-webpack-plugin');
    在plugins配置项中添加
    new WorkboxPlugin({
    clientsClaim: true,
    skipWaiting: true
    })
    在程序入口 index.js 中注册Serveic Worker
    if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js').then(registration => {
    console.log('SW registered: ', registration);
    }).catch(registrationError => {
    console.log('SW registration failed: ', registrationError);
    });
    });
    }
    运行 npm run build
    直接浏览器访问 http://127.0.0.1:8080 即可
05-18 09:43