webpack4入门

前提

  • 已安装node(版本号>4.0,已自带NPM)
  • mac机器
  • 有一个空目录

要实现的功能

  • JSX语法,ES6语法,LESS语法编写的模块转化为浏览器可执行的模块
  • 代码的混淆压缩
  • 多页面多入口
  • 提取公共文件

无webpack.config.js配置打包

  1. 快速构建package.json文件。
    npm init -y

  2. 安装webpack4及其命令行接口
    npm i webpack webpack-cli --save-dev

  3. package.json文件增加build参数

"scripts": {
  "build": "webpack"
}
  1. 创建./src/index.js文件
    增加内容
console.log(`这是入口文件`);
  1. 终端执行npm run build
    目录下多了一个./dist/main.js
    这个文件是webpack./src/index.js的打包结果。

productiondevelopment模式

  1. 修改package.json文件的scripts字段
"scripts": {
  "dev": "webpack --mode development",
  "build": "webpack --mode production"
}
  1. 分别执行npm run devnpm run build
    你会看到./dist/main.js不同的变化。
    production模式下,默认对打包的进行minification(文件压缩),Tree Shaking(只导入有用代码),scope hoisting(作用域提升)等等操作。
    总之是让打包文件更小。
    development模式下,对打包文件不压缩,同时打包速度更快。

如果没指定任何模式,默认是production模式。

ES6和React

  1. 安装对应依赖包
    npm i babel-core babel-loader babel-preset-env react react-dom babel-preset-react --save-dev

  2. 新建.babelrc文件,进行相关配置

{
  "presets": ["env", "react"]
}
  1. 新建webpack.config.js文件,进行相关配置
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};
  1. 新增./src/app.js以及修改./src/index.js
    ./src/app.js内容如下:
import React from "react";
import ReactDOM from "react-dom";
const App = () => {
  return (
    <div>
      <p>React here!</p>
    </div>
  );
};
export default App;
ReactDOM.render(<App />, document.getElementById("app"));

./src/index.js内容如下:

import App from "./App";
  1. 终端执行npm run build

使用html-webpack-plugin插件对html进行打包

新建./src/index.html文件,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>webpack4入门</title>
</head>
<body>
    <div id="app">
    </div>
</body>
</html>

安装依赖包。

npm i html-webpack-plugin html-loader --save-dev

修改webpack.config.js配置。

const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: "html-loader",
            options: { minimize: true }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: "./src/index.html",
      filename: "./index.html"
    })
  ]
};

终端执行npm run build命令。
你会看到项目多了个./dist/index.html文件。

使用webpack-dev-server插件

安装依赖包。
npm i webpack-dev-server --save-dev

修改package.json文件。

"scripts": {
  "start": "webpack-dev-server --mode development --open",
  "build": "webpack --mode production"
}

修改webpack.config.js文件,新增devServer配置。

devServer: {
    contentBase: require('path').join(__dirname, "dist"),
    compress: true,
    port: 8033,
    host: "127.0.0.1",
}

终端执行npm run start便可以启动webpack dev server

使用Hot Module Replacement

Hot Module Replacement有针对React,Vue,Redux,Angular,样式等等。
这里我们以React Hot Loader为例。

安装依赖包。

npm i react-hot-loader --save-dev

修改.babelrc文件,新增plugins选项。

{
  "plugins": ["react-hot-loader/babel"]
}

修改webpack.config.js文件。

const path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin");  
const webpack = require('webpack'); // 新增
module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader"
                }
            },
            {
                test: /\.html$/,
                use: [
                    {
                        loader: "html-loader",
                        options: { minimize: true }
                    }
                ]
            }
        ]
    },
    devtool: 'inline-source-map',
    plugins: [
        new HtmlWebPackPlugin({
            template: "./src/index.html",
            filename: "./index.html"
        }),
        new webpack.NamedModulesPlugin(), // 新增
        new webpack.HotModuleReplacementPlugin() //新增
    ],
    devServer: {
        contentBase: path.join(__dirname, "dist"),
        compress: true,
        port: 8033,
        host: "127.0.0.1",
        hot: true // 新增
    }
};

修改./src/app.js文件内容如下:

import React from "react";
import ReactDOM from "react-dom";
import { hot } from 'react-hot-loader' // 新增

const App = () => {
  return (
    <div>
      <p>这是一个测试文件!真得是动态更新啊</p>
      <div>好棒棒啊</div>
    </div>
  );
};
export default hot(module)(App); // 修改
ReactDOM.render(<App />, document.getElementById("app"));

终端执行npm run start便可以启动webpack dev server
然后修改./src/app.js看下效果。

动态加模块

  • 动态加载普通JS模块
    修改.babelrc文件添加"syntax-dynamic-import"插件
{
    "presets": [
        "env",
        "react"
    ],
    "plugins": ["react-hot-loader/babel","syntax-dynamic-import"]
}

然后在业务代码文件使用import语法即可

import('模块路径').then(mod => {
  someOperate(mod); //mod是module的简写,表示加载成功后的异步组件
});

如果没有使用syntax-dynamic-import插件会导致构建失败,并提示:

Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level
  • 动态加载React模块
    如同动态加载普通JS模块,修改.babelrc文件添加"syntax-dynamic-import"插件。
    然后安装react-loadable这个NPM包
npm install react-loadable --save

接着如下方式引入React组件模块即可。

import Loadable from 'react-loadable';

const LoadableBar = Loadable({
  loader: () => import('react组件模块路径'),
  loading() {
    return <div>Loading...</div>
  }
});

class MyComponent extends React.Component {
  render() {
    return <LoadableBar/>;
  }
}

多页面多入口打包

修改webpack.config.js如下

...
entry: {
    // 把html 加入 entry。目的是修改html文件也能hot reload。
    // 不要在生产环境这么做。因为这会导致 chunk 文件包含了无用的 html 片段。
    page0: ['./src/pages/page0/index.js','./src/template/page0.html'],
    page1: './src/pages/page1/index.js',
},
plugins: [
    new HtmlWebPackPlugin({
        template: "./src/template/page0.html",
        filename: "./page0.html",
        chunksSortMode: 'none',
        chunks: ["page0"],
    }),
    new HtmlWebPackPlugin({
        template: "./src/template/page1.html",
        filename: "./page1.html",
        chunksSortMode: 'none',
        chunks: ["page1"],
    }),
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
],  
...

对应文件存放目录结构修改如图


文件存放目录结构

第三方库和业务代码分开打包

基本策略是:

  1. 提取样式文件
  2. 分离公共文件(运行时,node_modules,业务的共同依赖)
  3. 懒加载大文件
    react-loadable
简单分离公共文件和业务文件
optimization: {
    splitChunks: {
        cacheGroups: {
            commons: {
                name: 'common',
                priority: 10,
                chunks: 'initial'
            }
        }
    }
}
分离业务文件,node_modules,业务的共同依赖,webpack运行时
runtimeChunk: {
    name: 'manifest'
},
optimization: {
    splitChunks: {
        cacheGroups: {
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                name: "vendors",
                chunks: "all"
            },
            commons: {
                chunks: "all",
                name: 'commons',
                /**
                 * minSize 默认为 30000
                 * 想要使代码拆分真的按照我们的设置来
                 * 需要减小 minSize
                 */
                minSize: 0,
                // 至少为两个 chunks 的公用代码
                minChunks: 2
            }
        }
    }
}

这里打包出来的文件有vendros.js(node_modules),commons.js(业务的共同依赖),manifest.js(webpack运行时),业务文件

手动指定业务的共同依赖
entry: {
    // html可实时刷新修改
    page0: ['./src/pages/page0/index.js', './src/template/page0.html'],
    page1: './src/pages/page1/index.js',
    page2: './src/pages/page2/index.js',
    pageCommon: ['./src/common/page0-1.js', './src/common/page0-2.js'],
},
optimization: {
    runtimeChunk: {
        name: 'manifest'
    },
    splitChunks: {
        cacheGroups: {
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                name: "vendors",
                chunks: "all"
            },
        }
    }
},
plugins: [
    new HtmlWebPackPlugin({
        template: "./src/template/page0.html",
        filename: "./page0.html",
        chunksSortMode: 'none',
        chunks: ["page0", "pageCommon", "vendors","manifest"],
    }),
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin(),
],

手动指定共同依赖后,就不要再让webpack自动分析共同依赖,否则手动的共同依赖打包出来不生效。

参考

https://www.valentinog.com/blog/webpack-4-tutorial/
https://github.com/fenivana/webpack-and-spa-guide
https://segmentfault.com/a/1190000014685887

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容