webpack3实战(5)打包一个多页、jQuery、图片转base6四、压缩混淆、异步模块加载的项目

前注:

文档全文请查看 根目录的文档说明javascript

若是能够,请给本项目加【Star】和【Fork】持续关注。css

有疑义请点击这里,发【Issues】。html

实战项目示例目录前端

0、使用说明

安装:java

npm install
复制代码

运行(注,这里不像以前用的 test ,而是改用了 build):jquery

npm run build
复制代码

一、需求列表

基本需求:webpack

  1. 引入jQuery(或其余相似库,之因此用 jQuery 是每一个前端开发者都理应会 jQuery);
  2. 使用 less 做为 css 预处理器;
  3. 标准模块化开发;
  4. 有异步加载的模块;
  5. 使用 es六、es7 语法;
  6. 写一个登陆页面做为DEMO,再写一个登陆后的示例页面做为跳转后页面;
  7. 可适用于多页项目;
  8. css 文件与 图片 文件脱离(即更改 css 文件路径不影响其对图片的引用)

打包要求:git

  1. 启用 hash 命名,以应对缓存问题;
  2. css 自动添加兼容性前缀;
  3. 将图片统一放到同一个文件夹下,方便管理;
  4. 将共同引入的模块单独打包出来,用于缓存,减小每次重复加载的代码量;
  5. 代码进行丑化压缩;

二、涉及到的知识

  1. 入口:设置入口文件;
  2. 出口:设置打包后的文件夹以及文件命名;
  3. babel-loader:用于将es六、es7等语法,转换为es5语法;
  4. css-loader:用于处理css文件(主要是处理图片的url);
  5. style-loader:将转换后的css文件以 style 标签形式插入 html 中;
  6. postcss-loader:通常用于添加兼容性属性前缀;
  7. less-loader:以 less 语法来写 css ;
  8. url-loader:用于将图片小于必定大小的文件,转为 base64 字符串;
  9. file-loaderurl-loader 不能转换 base64字符串 的文件,被这个处理(主要用于设置打包后图片路径,以及CDN等);
  10. html-withimg-loader:用于加载html模板;
  11. html-webpack-plugin :用于将已有 html 文件做为模板,生成打包后的 html 文件;
  12. clean-webpack-plugin:用于每次打包前清理dist文件夹
  13. CommonsChunkPlugin:提取 chunks 之间共享的通用模块

三、技术难点

3.一、多页面

多页模式是一个难点。es6

且不考虑共同模块(这里主要指的是html模板,而不是js的模块),光是单独每一个入口 js 文件须要搭配一个相对应的 html 文件,就已是一件很麻烦的事情了。github

对于这个问题,须要借助使用 html-webpack-plugin 来实现。

因为以前木有 html-webpack-plugin 的相关内容,这里只讲思路和代码。

第一:多入口则多个html文件

也是核心内容,html-webpack-plugin 只负责生成一个 html 文件。

而多入口显然须要生成多个 html 文件,所以 有多少个入口,就须要在 webpack 的 plugins 里添加多少个 html-webpack-plugin 的实例。

同时,咱们还要更改 webpack 的 entry 入口,entry 的值应该是根据入口数量自动生成的对象。

第二:chunks特性实现按需加载

经过配置 html-webpack-pluginoptions.chunks ,可让咱们实现让 login.html 只加载 login/index.js,而 userInfo.html 只加载 userInfo/index.js(注:因为以 entry 的 key 做为寻找出口文件的根据,所以打包后带 hash 的文件名不影响匹配);

注意,这个实现的机制,是经过 options.chunk 的值,去匹配 webpack.config.jsentry 对象的 key

由于一个入口文件对应一个出口文件,因此这里会去拿入口文件对应的出口文件,将其加到 html 文件里。

第三:template自定义做为模板的 html 文件

options.template 能够自定义该实例以哪一个 html 文件做为模板。

第四:filename

options.filename 能够自定义生成的 html 文件输出为何样的文件名。

第五:管理多入口

已知:

一个 html-webpack-plugin 实例具备如下功能:

  1. 生成一个 html 文件(一);
  2. 决定本身引入哪一个 js 文件(二)(记得,webpack只负责打包js文件,不负责生成 html 文件。生成实例是依靠这个 plugins);
  3. 决定本身以哪一个 html 文件做为模板(三);
  4. 决定本身打包后的目录和文件名(四);

咱们经过webpack打包后,一个入口 js 文件会对应一个出口 js 文件;

而每一个入口 js 文件,都对应一个 html 模板文件;

所以每一个 html 模板文件,都知道本身对应哪一个出口 js 文件;

因此以上是实现多入口的原理。

代码:

多入口管理文件:

config/entry.json

[
    {
        "url": "login",
        "title": "登陆"
    },
    {
        "url": "userInfo",
        "title": "用户详细信息"
    }
]
复制代码

webpack配置文件:

webpack.config.js:

首先,配置 entry

const entryJSON = require('../config/entry.json');

// 入口管理
let entry = {}
entryJSON.map(page => {
    entry[page.url] = path.resolve(__dirname, `../src/page/${page.url}/index.js`)
})
复制代码

其次,配置 plugins

// 在上面已经引用了 entryJSON
const path = require('path')

// 由于多入口,因此要多个HtmlWebpackPlugin,每一个只能管一个入口
let plugins = entryJSON.map(page => {
    return new HtmlWebpackPlugin({
        filename: path.resolve(__dirname, `../dist/${page.url}.html`),
        template: path.resolve(__dirname, `../src/page/${page.url}/index.html`),
        chunks: [page.url], // 实现多入口的核心,决定本身加载哪一个js文件,这里的 page.url 指的是 entry 对象的 key 所对应的入口打包出来的js文件
        hash: true, // 为静态资源生成hash值
        minify: false,   // 压缩,若是启用这个的话,须要使用html-minifier,否则会直接报错
        xhtml: true,    // 自闭标签
    })
})
复制代码

最后,webpack 自己的配置:

module.exports = {
    // 入口文件
    entry: entry,
        // 出口文件
    output: {
        path: __dirname + '/../dist',
        // 文件名,将打包好的导出为bundle.js
        filename: '[name].[hash:8].js'
    },
    // 省略中间的配置
    // 将插件添加到webpack中
    plugins: plugins
}
复制代码

文件目录(已省略无关文件):

├─build
│  └─webpack.config.js
├─dist
└─src
    └─page
       ├─login
       │  ├─index.js
       │  ├─index.html
       │  └─login.less
       └─userInfo
          ├─index.js
          └─index.html
复制代码

3.二、文件分类管理

如何将页面整齐的分类,也是很重要的。不合理的规划,会增长项目的维护难度。

项目目录以下分类:

├─build     webpack 的配置文件,例如 webpack.config.js
├─config    跟 webpack 有关的配置文件,例如 postcss-loader 的配置文件,以及多入口管理文件
├─dist      打包的目标文件夹,存放 html 文件
│  └─img    打包后的图片文件夹
└─src       资源文件夹
    ├─common    全局配置,或者公共方法,放在此文件夹,例如 less-loader 的全局变量
    ├─img       图片资源文件夹,这些是共用的图片
    ├─less      less 文件夹,共用的less文件
    ├─page      每一个页面,在page里会有一个文件夹,里面放置入口 js 文件,源 html 文件,以及不会被复用的 html template文件。
    ├─template  html 模板文件夹(经过js引入模板,这里的可能被复用)
    └─static    静态资源文件夹,这里放使用静态路径的资源
复制代码

虽然还不够精细,但应对小型项目是足够了的。

3.三、别名

别名的优点不少,好比:

一、css/less 代码,能够和图片分离:

只要 webpack 配置和图片的位置不变。

那么使用别名,就能够随意移动 less 文件。

没必要担忧由于移动 less 文件,而形成的 less 文件与 图片 文件的相对路径改变,致使找不到图片而出错。

二、方便总体移动图片

假如本来图片放在src/img文件夹下,如今你忽然想把图片放在src/image文件夹下。

若是不使用别名,你须要一个一个去修改图片的路径;

而使用别名,只须要改一下别名的路径就好了。

css-loader 支持独立于 webpack 的别名的设置,教程参照:css-loader

这里基于【3.2】的文件分类管理,附上关于别名的控制代码:

{
    loader: 'css-loader',
    options: {
        root: path.resolve(__dirname, '../src/static'),   // url里,以 / 开头的路径,去找src/static文件夹
        minimize: true, // 压缩css代码
        // sourceMap: true,    // sourceMap,默认关闭
        alias: {
            '@': path.resolve(__dirname, '../src/img') // '~@/logo.png' 这种写法,会去找src/img/logo.png这个文件
        }
    }
},
复制代码

其他代码已省略,若是有须要,请查看 DEMO 中的 build/webpack.config.js 文件。

3.四、安装jQuery

方案:

因为npm上并无最新的 jQuery,目前来讲, 1.7.4 是最新的版本。

因此能够从下面这个CDN直接下载 jQuery 来使用,版本是 1.12.4

https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js

而后在js文件的开始位置,经过require引入(注意,不能经过 import 引入)

const $ = require('../../common/jquery.min')
复制代码

webpack会帮你作剩下的事情,你只须要愉快的使用 jQuery 就行了。

3.五、提取 chunks 之间共享的通用模块

在 3.4 中,咱们引入了 jQeury,方法简单易行,但又一个缺点,那就是会致使代码重复打包的问题。

即 jQuery 会被打包进每个引入他的入口 js 文件中,每一个页面都须要重复下载一份将jQuery代码打包到其中的 js 文件(极可能两个 js 文件只有 20kb 是本身的代码,却有 90kb 是 jQuery 代码)。

咱们指望:

  1. 访问第一个页面时,预期加载 foo.js 和 jQuery.js;
  2. 访问第二个页面时,预期加载 bar.js 和 jQuery.js;
  3. 当访问第二个页面时,发现已经在第一个页面下载过 jQuery.js 了,所以将不须要再下载 jQuery 代码,只须要下载 bar.js 就能够了;

方案改进:

为了实现这个目标,毫无疑问,咱们须要将 jQuery.js 文件单独打包,或者说,每个在多个模块中共享的模块,都会被单独打包。

有几种作法,但实测后都很差用,鉴于 jQuery 会在每一个页面都适用,所以综合考虑后,我采用如下方案来初步实现个人目标。

最后我采用了 webpack 自带的插件:webpack.optimize.CommonsChunkPlugin来实现,他能够将在多个文件中引入的 模块,单独打包。

关于这个插件能够先参考官方文档:CommonsChunkPlugin: 提取 chunks 之间共享的通用模块

为了实现咱们的目的,咱们须要作两件事:

一、使用这个插件,以下配置:

const webpack = require('webpack')

new webpack.optimize.CommonsChunkPlugin({
    name: "foo", // 这个对应的是 entry 的 key
    minChunks: 2
})
复制代码

这个的效果是将至少有 2 个 chunk 引入的公共代码,打包到 foo 这个 chunk 中。

二、咱们须要引入这个打包后的 chunk ,方法是经过 html-webpack-plugin 这个插件引入。

// 无关配置已经省略
new HtmlWebpackPlugin({
    chunks: [page.url, 'foo'], // 这里的foo,就是经过CommonsChunkPlugin生成的chunk
})
复制代码

无需修改源代码,此时咱们能够执行npm run build查看打包后的效果:

foo.d78e8f4193f50cc42a49.js    // 199 KB(这里包含jQuery以及公共代码)
login.d2819f642c5927565e7b.js  // 15 KB
userInfo.1610748fb3346bcd0c47.js // 4 KB
0.fe5c2c427675e10b0d3a.js      // 2 KB
复制代码

注:

若是页面不少的话,那么极可能某些公共组建被大量chunk所共享,而某些chunk又被少许chunk所共享。

所以可能须要特殊配置 minChunks 这个属性,具体请查看官方文档。

3.六、每次打包前,清理dist文件夹

须要借助 clean-webpack-plugin 这个插件。

使用这个插件后,能够在每次打包前清理掉整个文件夹。

基于本项目来讲,清除的时候配置的时候须要这样配置:

new CleanWebpackPlugin(path.resolve(__dirname, '../dist'), {
    root: path.resolve(__dirname, '../'),    // 设置root
    verbose: true
})
复制代码

缘由在于,这个插件会认为webpack.config.js所在的目录为项目的根目录。

只使用第一个参数的话,会报错移除目标的目录位置不对:

clean-webpack-plugin: (略)【实战5】打包一个具备常见功能的多页项目\dist is outside of the project root. Skipping...
复制代码

而添加了第二个参数的设置后,就能够正常使用了。

注:

他的效果是直接删除文件夹,所以千万别写错目录了,若是删除了你正常的文件夹,那么……就只能哭啦。

3.七、使用 html 模板

因为咱们极可能在 html 中使用 <img> 标签,

html-webpack-plugin 这个插件,只能用于将某个 html 文件做为打包后的源 html 文件,

不会将其 <img> 标签中的 src属性转为打包后的图片路径,同时也不会将引入的图片进行打包。

所以咱们须要将 html 内容单独拆出来,page 文件夹里的源文件只负责做为 html 模板而已。

为了使用 html 模板,咱们须要专门引入一个插件:

html-withimg-loader:用于解析 html 文件。

使用方法很简单:

  1. 配置loader(参照 webpack.config.js);
  2. import 导入 html 模板文件(例如 login.html);

导入的时候,是一个字符串,而且图片的 url 已经被解析了。而后咱们将其引入源 html 文件中(好比page/login.html),再写各类逻辑就好了。

注:

务必记得先把 html 模板插入页面中,再写他的相关逻辑。

3.八、代码的丑化压缩

使用插件 UglifyjsWebpackPlugin ,文档参照 (UglifyjsWebpackPlugin)[https://doc.webpack-china.org/plugins/uglifyjs-webpack-plugin]

压缩前:

0.fe5c2c427675e10b0d3a.js    // 2 KB
foo.a5e497953a435f418876.js    // 199 KB
login.9698d39e5b8f6c381649.js    // 15 KB
userInfo.f5a705ffcb43780bb3d6.js    // 4 KB
复制代码

丑化压缩后:

0.fe5c2c427675e10b0d3a.js    // 1 KB
foo.a5e497953a435f418876.js    // 120 KB
login.9698d39e5b8f6c381649.js    // 10 KB
userInfo.f5a705ffcb43780bb3d6.js    // 2 KB
复制代码

四、分析

从新列出全部需求:

基本需求:

  1. 引入jQuery(或其余相似库,之因此用 jQuery 是每一个前端开发者都理应会 jQuery);
  2. 使用 less 做为 css 预处理器;
  3. 标准模块化开发;
  4. 有异步加载的模块;
  5. 使用 es六、es7 语法;
  6. 写一个登陆页面做为DEMO,再写一个登陆后的示例页面做为跳转后页面;
  7. 可适用于多页项目;
  8. css 文件与 图片 文件脱离(即更改 css 文件路径不影响其对图片的引用)

打包要求:

  1. 启用 hash 命名,以应对缓存问题;
  2. css 自动添加兼容性前缀;
  3. 将图片统一放到同一个文件夹下,方便管理;
  4. 将共同引入的模块单独打包出来,用于缓存,减小每次重复加载的代码量;
  5. 代码进行丑化压缩;

需求的实现:

基本需求:

需求的实现过程
需求 实现方法
引入jQuery 1. 经过 require() 引入,并经过 CommonsChunkPlugin 实现单独打包;
使用 less 做为 css 预处理器 1. 使用 less-loader 来处理 .less 文件;
标准模块化开发 1. 使用 import 和 require 语法来进行模块化开发;
有异步加载的模块 1. 经过 require([], callback) 来实现模块的异步加载
使用 es六、es7 语法 1. 使用 babel 来转义
写一个登陆页面做为DEMO,再写一个登陆后的示例页面做为跳转后页面 1. 登陆页:page/login
2. 跳转后页面:page/userInfo
可适用于多页项目 1. config/entry.json 用于配置多页入口;
2. html-withimg-loader 来生成多页模板;
3. 最后在webpack.config.js里配置 entry 和 plugins
css 文件与 图片 文件脱离(即更改 css 文件路径不影响其对图片的引用) 经过 css-loader 的别名实现

打包需求:

需求的实现过程
需求 实现方法
启用 hash 命名,以应对缓存问题 配置 output 的 filename 属性,加 [chunkhash] 便可
css 自动添加兼容性前缀 使用 post-loader 的 autoprefixer
将图片统一放到同一个文件夹下,方便管理 配置 url-loader (实质是 file-loader )的 outputPath
将共同引入的模块单独打包出来,用于缓存,减小每次重复加载的代码量 使用插件 CommonsChunkPlugin 来实现
代码进行丑化压缩 使用插件 UglifyjsWebpackPlugin 来实现
相关文章
相关标签/搜索