整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Webpack 模块化开发与配置

Webpack 模块化开发与配置

ebpack 模块化的配置,包括处理 js转换 / css / html / eslint / 图片等的 loader、plugin 配置。 传说中前端配置工程师的基本修养……

一 模块(Module)

模块是为了完成某种功能所需的程序或子程序,模块是系统中职责单一且可替换的部分。

模块化就是把系统代码分为一系列职责单一且可替换的模块。模块化开发是指如何开发新的模块和复用已有的模块来实现应用的功能。

javascript 的三大主流模块规范已在上篇介绍过,这么不多赘述。

Webpack 中一切皆模块,包括 javascript、css、html、图片、字体、富媒体及 less、sass、 各种 javascript 库等等,webpack 可以针对不同的模块用不同的 loader 做不同的解析编译,做到按需加载。比如对于 less 代码,可以先用 less-loader 转换为 css 代码,用 css-loader 处理其中的 url/import 等逻辑,最后用 style-loader 把 css 插入到 <style> 标签里。

1. Webpack对模块的增强:import() 和神奇注释(Magic Comments)

Webpack 导入模块的方式不限于三大主流模块规范,提供了 import() 引入模块的方式。

与 import from 不同的是, import from 是静态分析打包的,而 import() 是动态分析打包的,通过异步的方式加载模块,import() 返回一个 Promise 对象。并且 import() 被视为分割点,被请求的模块和它引用的所有子模块,打包后会分割到一个单独的 chunk 中。

// 想象我们有一个从 cookies 或其他存储中获取语言的方法
const language=detectVisitorLanguage();
import(`./locale/${language}.json`).then((module)=> {
  // do something with the translations
});

神奇注释(Magic Comments),是指可以通过在 import() 中添加注释实现对打包的文件做一定的操作,比如给 chunk 命名、选择不同模式。

// 单个目标
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  'module'
);

// 多个可能的目标
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);

Magic Comments

说明

webpackChunkName

chunk 文件的名称

webpackInclude

在导入解析过程中,用于匹配的正则表达式,只有匹配到的模块才会被打包

webpackExclude

用于匹配的正则表达式,匹配到的模块不会被打包

webpackMode

指定以不同的模式解析动态导入,可选值lazy/lazy-once/eager/weak

webpackPrefetch

是否预取模块,及其优先级

webpackPreload

是否预加载模块,及其优先级

通过神奇注释,import()不再是简单的 JavaScript 异步加载器,还是任意模块资源的加载器,举例说明:如果我们页面用到的图片都放在src/assets/img文件夹下,你们可以通过下面方式将用到的图片打包到一起:

import(/* webpackChunkName: "image", webpackInclude: /\.(png|jpg|gif)/ */ './assets/img');

2. Webpack对模块的增强:require

require.context()

require.context(directory, includeSubdirs, filter)可以批量将 directory内的文件全部引入进文件,并且返回一个具有resolve的 context 对象,使用context.resolve(moduleId)则返回对应的模块。该功能十分有用。

  • directory:目录 string;
  • includeSubdirs:是否包含子目录,可选,默认值是 true;
  • filter:过滤正则规则,可选项。

require.context() 会将所有的文件都引入进 bundle。

require.resolve()

require.resolve(dependency: String)可以获取模块的唯一 ID, 并把模块真实引入 bundle。

require.include()

require.include(dependency: String)引入一个不需要执行的 dependency,这样可以用于优化输出 chunk 中依赖模块的位置。

/**
 * 输出:
* entry chunk: file.js and a
* anonymous chunk: b
* anonymous chunk: c
* 不使用 require.include('a'),输出的两个匿名 chunk 都会有模块 a
*/
require.include('a');
require.ensure(['a', 'b'], function (require) {
  /* ... */
});
require.ensure(['a', 'c'], function (require) {
  /* ... */
});

二 使用 Babel 转换 js 代码

在 webpack 中编写 JavaScript 代码,可以使用最新的 ES 语法,而最终打包的时候,webpack 会借助 Babel 将 ES6+语法转换成在目标浏览器可执行 ES5 语法。

Babel 拥有自己的 cli,可以单独安装使用,具体参考 Babel 官网,这里不赘述。

在 npm 项目中,Babel 支持两种文件配置方式:

  • 在 package.json 中配置 babel 属性
  • 在项目根目录下单独创建 .babelrc 或者 .babelrc.js 文件
// package.json
{
    "name": "my-package",
    "version": "1.0.0",
    "babel": {
        "presets": ["@babel/preset-env"]
    }
}
// .babelrc
{
    "presets": ["@babel/preset-env"]
}

如果使用了webpack,也可以配置在 webpack 的配置文件中:

// 安装开发依赖
npm i webpack babel-loader webpack-cli @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
// 将 runtime 作为依赖
npm i @babel/runtime -S

// 配置示例
module.exports={
    entry: './babel.js',
    mode: 'development',
    devtool: false,
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        useBuiltIns: 'usage'
                                    }
                                ]
                            ]
                        }
                    }
                ]
            }
        ]
    }
};

1. env 选项

配置 env 选项可以实现在不同环境下使用不同的 Babel 配置。

env 选项的值将从 process.env.BABEL_ENV 获取,如果没有的话,则获取 process.env.NODE_ENV 的值,如果这个也没有,会默认设置为 "development"。

{
  "env": {
    "production": {
      "presets": ["@babel/preset-env"]
    }
  }
}

2. Polyfill

polyfill:在 JavaScript 中表示一些可以抹平浏览器实现差异的代码,比如某浏览器不支持 Promise,可以引入es6-promise-polyfill 等库来解决。

Babel 的插件分为两类:转换插件和语法解析插件。转换插件用于对语法进行转换,如 ES6 转换为 ES5,语法解析插件用来扩展语法,如解析 React 特殊设计的jsx语法。对于各种情况下的语法转换与解析,需要一个一个的引入相应的插件,这种方式比较麻烦,一般场景下,可以直接使用官方推出的插件组合@babel/preset-env。

@babel/preset-env可以根据开发者的配置按需加载对应的插件,还可以根据代码执行平台环境和具体浏览器版本生成相应的 js 代码。

@babel/preset-env有两个重要的配置项:useBuiltIns和target。

/**
 * useBuiltIns: usage|entry|false,一般采用 usage 这种方式,即指定按需加载。
 * target: 指定构建目标
 */
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "usage|entry|false"
      "targets": {
          // "browsers": "IE 10" // 指定IE 10浏览器
          "node": "8.9.3" // 如果在node环境中,指定node版本号
      }
    }]
  ]
}

Browserslist 目标浏览器的设置

项目的实际开发与运行中,很多情况下要做一定的浏览器兼容,而 Browserslist 就是用来设置目标浏览器的工具。许多开发工具包括 npm、babel 都兼容内置了 Browserslist。

Browserslist 的配置可以放在 package.json 中,也可以单独放在配置文件.browserslistrc 中。相关的开发工具都会主动查找 browserslist 的配置文件,根据 browserslist 配置找出对应的目标浏览器集合。

// package.json 内配置
{
    "browserslist": ["last 2 version", "> 1%", "maintained node versions", "not ie < 11"]
}
// 兼顾不同环境的配置,设置BROWSERSLIST_ENV 或者 NODE_ENV可以配置不同的环境变量
{
    "browserslist": {
        "production": ["> 1%", "ie 10"],
        "development": ["last 1 chrome version", "last 1 firefox version"]
    }
}
// 项目的根目录下.browerslistrc文件:
// 注释是这样写的,以#号开头
// 每行一个浏览器集合描述
last 2 version
> 1%
maintained node versions
not ie < 11

// 兼顾不同环境的配置
[production staging]
> 1%
ie 10

[development]
last 1 chrome version
last 1 firefox version

常见的浏览器集合范围说明:

范围

说明

last 2 versions

caniuse.com网站跟踪的最新两个版本,假如 iOS 12 是最新版本,那么向后兼容两个版本就是 iOS 11 和 iOS 12

> 1%

全球超过 1%人使用的浏览器,类似> 5% in US则指代美国 5%以上用户

cover 99.5%

覆盖 99.5%主流浏览器

chrome > 50 ie 6-8

指定某个浏览器版本范围

unreleased versions

说有浏览器的 beta 版本

not ie < 11

排除 ie11 以下版本不兼容

since 2013 last 2 years

某时间范围发布的所有浏览器版本

maintained node versions

所有被 node 基金会维护的 node 版本

current node

当前环境的 node 版本

dead

通过last 2 versions筛选的浏览器中,全球使用率低于0.5%且官方声明不在维护或者事实上已经两年没有再更新的版本

defaults

默认配置,> 0.5% last 2 versions Firefox ESR not dead

浏览器名称列表(大小写不敏感):

浏览器

说明

浏览器

说明

Chrome

chrome浏览器

ChromeAndroid/and_chr

chrome 安卓移动浏览器

Edge

Edge浏览器

Explorer/ie

ie浏览器

Android

安卓webview浏览器

Baidu

百度浏览器

Firefox/ff

火狐浏览器

FirefoxAndroid/and_ff

火狐安卓浏览器

iOS/ios_saf

iOS Safari 浏览器

ExplorerMobile/ie_mob

ie 移动浏览器

Safari

桌面版本 Safari

Node

nodejs

Opera

opera浏览器

OperaMobile/op_mob

opera 移动浏览器

OperaMini/op_mini

operaMini 浏览器

QQAndroid/and_qq

QQ 安卓浏览器

Samsung

三星浏览器

UCAndroid/and_uc

UC 安卓浏览器

Electron

Electron

BlackBerry / bb

黑莓浏览器

Babel Polyfill 的最佳实践:

useBuiltIns:'usage'可以近乎完美的解决我们的 Polyfill 问题,它是按需引入模块,根据 .browserslist + 业务实际代码来设置引入 Polyfill,不会多余的引入。

三 样式相关配置

Webpack 中一切皆模块,经 loader 处理后的 CSS 可以在 js 中被直接引用。

1. css-loader 与 style-loader

// 引入 css-loader
npm install --save-dev css-loader

// webpack.config.js 配置loader
{
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['css-loader']
            }
        ];
    }
}

// 代码中引入 css 模块
import css from './css/index.css';
console.log(css);

// 或者不用webpack配置文件,直接在引入语句中使用loader
import css from 'css-loader!./css/index.css';
console.log(css);

经 css-loader 处理后,css 模块被打包转换成 js 对象,可直接用 js 语法使用。

style-loader 的作用是将 css-loader 打包好的 css 代码以<style>标签的形式插入到 html 文件中,一般情况下,在一个项目中 style-loader 与 css-loader 是成对出现的,并且 style-loader 要在 css-loader 之后。

npm install --save-dev style-loader

2. mini-css-extract-plugin

CSS 作为<style>标签放到 HTML 内还是不够的,我们还需要将 CSS 以 <link> 的方式通过 URL 的方式引入进来,这时候就需要使用 mini-css-extract-plugin 。

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
module.exports={
    plugins: [
        // 添加 plugin
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        })
    ],
    module: {
        rules: [
            {
                test: /\.css$/,
                // 添加 loader
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            }
        ]
    }
};

3. CSS Modules

CSS Modules 指的是所有的 CSS 类名及其动画名都只是局部作用域的 CSS 文件。CSS Modules 主要解决的问题有:

  • 解决 CSS 类都是全局的,容易造成全局污染(样式冲突);
  • JS 和 CSS 共享类名;
  • 可以方便的编写出更加健壮和扩展方便的 CSS。

CSS Module 的表现形式:

/* app.css */
.element {
    background-color: blue;
    color: white;
    font-size: 24px;
}
/* app.js */
// 引入 app.css
import styles from './app.css';
// js 直接使用 CSS 的类名作为对象值
let element=`
  <div class="${styles.element}">
    <p>CSS Modules</p>
  </div>
`;
document.write(element);

CSS Modules 在 webpack.config.js 中的配置:

// CSS Modules 相关的配置还有很多,可以查看css-loader对应的文档。
module.exports={
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    }
                ]
            }
        ]
    }
};

4. CSS 预处理器

常见的 CSS 预处理器有 Less、Sass 及其语法变种 Scss和Stylus。预处理器为 CSS 提供了变量、函数、运算、作用域、继承、嵌套等语法,使其功能更加强大。

以 less-loader 为例,less-loader 将 Less 语法编译成 CSS,后续还需要使用 css-loader 和 style-loader 处理才可以,所以一般来说需要配合使用:

module.exports={
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    },
                    'less-loader' // 将 Less 编译为 CSS
                ]
            }
        ]
    }
};

一些预处理语言需要安装对应的解析器,例如 sass-loader,需要同时安装 node-sass。

npm install sass-loader node-sass --save-dev

5. PostCSS:CSS 后处理器

表现形式:

/*没有前缀的写法*/
.flex {
    display: flex;
}

/*经过 postcss autoprefixer 处理后*/
.flex {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
}

PostCss 能实现的功能很多,除了添加前缀,还可以最新语法转义、压缩等,甚至可以扩展 CSS 的语言特性。配置了 postcss-loader 之后,WebPack 就可以使用 PostCSS 来处理 CSS了,但是 PostCSS 本身只不过是将 CSS 解析成 AST ,真正起作用的还需要依赖其强大的插件系统。

PostCSS 配置其实主要是配置其使用哪些插件,PostCSS 的配置写法有以下三种方式:

  • 通过配置文件postcss.config.js,一般放置在项目的根目录下;
  • 通过 loader 的配置项 options;
  • 直接在 package.json 中添加个 postcss 属性。
/* postcss.config.js */
// 引入postcss 插件
const autoprefixer=require('autoprefixer');
module.exports={
    plugins: [autoprefixer(['IE 10'])]
};
/* webpack.config.js */
// 引入postcss 插件
const autoprefixer=require('autoprefixer');
module.exports={
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            // 通过 plugins 选项
                            plugins: [autoprefixer(['IE 10'])]
                        }
                    }
                ]
            }
        ]
    }
};
/* package.json:受限于 json 的语法,可扩展性较弱,一般不推荐 */
{
    "postcss": {
        "plugins": {
            "autoprefixer": "IE 10"
        }
    }
}

6. 常见的 PostCss 插件

以下罗列几种常用的 PostCSS 插件,详细用法参见各自的官网或相关文档。

Autoprefixer

给 css 补齐各种浏览器私有的前缀,例如 -webkit、-moz、-ms 等,当然还会处理各种兼容性问题,比如 flex 语法,不能简单添加 -webkit 就解决,还需要处理成 -webkit-box 这类老版本的标准。

Autoprefixer 的主要参数就是 browserslist。

postcss-preset-env

postcss-preset-env 是跟 babel 的 preset-env 类似的功能,不用一一引入要用的插件,在打包构建的时候,会根据不同的配置输出对应支持的 CSS 文件。

PreCSS

可以写类似 Sass 和 cssnext 语法的 CSS。

cssnano

CSS 压缩优化。

四 代码规范工具配置

1. ESLint

npm install eslint --save-dev

安装之后,可以通过使用 npx eslint 直接运行,在运行之前,我们需要创建个 ESLint 的配置文件,使用 eslint --init 命令创建 .eslintrc.json 文件。

.eslintrc.json 内容示例,具体可查看 eslint 官网文档:

{
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": "airbnb-base",
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
    },
    "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "rules": {
        // 禁止 console,要用写 eslint disbale
        'no-console': 2,
        // 禁止 debugger,防止上线
        'no-debugger': 2,
        // 禁止 alert,要用写 eslint disable
        'no-alert': 2,
        // 不用的 var,要删除,手动 tree shaking,要洁癖
        'no-unused-vars': 2,
        // 没定义就用的就别用,全局的要用 写 eslint global
        'no-undef': 2
    }
}

ESLint 的报错类型包括三种:off、warn和error,分别对应着:0、1、2,所以上面的配置的 rule 实际为error级别的规则,检测到了则直接报错误(Error)。

webpack 中的配置:

npm install  eslint-loader --save-dev
module.exports={
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'eslint-loader',
                enforce: 'pre', // 调整了 loader 加载顺序,保证先检测代码风格,之后再做 Babel 转换等工作
                include: [path.resolve(__dirname, 'src')], // 指定检查的目录
                options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine
                    formatter: require('eslint-friendly-formatter') // 指定错误报告的格式规范
                }
            }
        ]
    }
};

2. StyleLint

StyleLint 的配置文件是.stylelintrc.json,其中的写法跟 ESLint 的配置类似

npm install -D stylelint
/* .stylelintrc.json */
{
    "extends": ["stylelint-config-standard", "stylelint-config-recess-order"],
    "rules": {
        "at-rule-no-unknown": [true, {"ignoreAtRules": ["mixin", "extend", "content"]}]
    }
}

webpack 中配置:

const StyleLintPlugin=require('stylelint-webpack-plugin');

module.exports={
    // ...
    plugins: [new StyleLintPlugin(options)]
    // ...
};

默认 StyleLint-webpack-plugin 会查找项目中的 StyleLint 配置文件,根据配置文件的配置来检测 CSS 代码。

五 静态资源管理

前端的静态资源一般指图片、字体文件、富媒体等。

1. 图片引入方式

图片引入的一般方式有,Html 引入,Css 引入和 Javascript 的引入。在 webpack 中可以使用 loader 来处理加载图片。

file-loader 与 url-loader

这是最常见的处理图片的两种 loader。它俩区别在于:

  • file-loader: 能够根据配置项复制使用到的资源和构建之后的文件夹,并且能够更改对应的链接
  • url-loader: 包含 file-loader 的全部功能,并且能欧根据配置将文件转换成 Base64 的形式,以此减少 http 请求,实现一定程度上的优化。如果图片较大,Base64 会很长,会导致打包后的 js/css 文件很大。

webpack 的配置如下,使用 publicPath 可做静态资源的 CDN 处理,可以使用 resolve.alias 做别名处理:

// webpack.config.js
const HTMLPlugin=require('html-webpack-plugin');

module.exports={
    mode: 'development',
    entry: './src/index.js',
    // output: {
    //     publicPath: 'http://bd.bxstatic.com/img/'
    // }
    devtool: false,
    module: {
        // resolve: {
        //     alias: {
        //         '@assets': path.resolve(__dirname, './src/assets')
        //     }
        // },
        rules: [
            {
                test: /\.html$/,
                use: ['html-loader']
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        // url-loader 默认会做 Base64 处理,这里限制不超过 3k 大小的图片才做 Base64 处理
                        limit: 3*1024
                    }
                }
            }
        ]
    },
    plugins: [
        new HTMLPlugin({
            template: './src/index.html'
        })
    ]
};

在 Html 和 Css 中用别名的方式引入图片时,要在别名前加~符号:

<img src="~@assets/img/large.png" alt="背景图" />
.bg-img {
    background: url(~@assets/img/small.png) no-repeat;
}

svg-url-loader

svg-url-loader 的工作原理类似于 url-loader,主要用来处理 SVG 文件。 svg-url-loader 拥有改善 IE 浏览器支持的选项,但是在其他浏览器中很糟糕。如果你需要兼容 IE 浏览器,设置 iesafe: true选项。

module.exports={
    module: {
        rules: [
            {
                test: /\.svg$/,
                loader: 'svg-url-loader',
                options: {
                    // 小于 10kB(10240字节)的内联文件
                    limit: 10 * 1024,
                    // 移除 url 中的引号
                    // (在大多数情况下它们都不是必要的)
                    noquotes: true
                }
            }
        ]
    }
};

img-webpack-loader

img-webpack-loader 用来对图片进行压缩优化,支持 JPG、PNG、GIF 和 SVG 格式的图片,不过必须和 url-loader 以及 svg-url-loader 一起使用。

module.exports={
    module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif|svg)$/,
                loader: 'image-webpack-loader',
                //提高优先级,保证在url-loader和svg-url-loader之前完成图片优化
                enforce: 'pre'
            }
        ]
    }
};

postcss-sprites

将小图标合成雪碧图。需要结合 postcss-loader 使用。

// postcss.config.js
const postcssSprites=require('postcss-sprites');

module.exports={
    plugins: [
        postcssSprites({
            // 在这里制定了从哪里加载的图片被主动使用css sprite
            // 可以约定好一个目录名称规范,防止全部图片都被处理
            spritePath: './src/assets/img/'
        })
    ]
};
//webpack.config.js
module.exports={
    module: {
        rules: [
            {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                'css-loader',
                {
                    loader: 'postcss-loader'
                }
            ]
        }
        ]
    }
};

2. 字体、富媒体处理

可以直接使用 url-loader 或者 file-loader 进行配置即可,不需要额外的操作。如果不需要 Base64,那么可以直接使用 file-loader,需要的话就是用 url-loader。

{
    // 文件解析
    test: /\.(eot|woff|ttf|woff2|appcache|mp4|pdf)(\?|$)/,
    loader: 'file-loader',
    query: {
        // 这么多文件,ext不同,所以需要使用[ext]
        name: 'assets/[name].[hash:7].[ext]'
    }
}

3. 数据

类似 CSV、TSV 和 XML 等数据,那么我们需要单独给它们配置相应的 loader。内置支持 JSON 数据。

 {
    test: /\.(csv|tsv)$/,
    use: [
    'csv-loader'
    ]
},
{
    test: /\.xml$/,
    use: [
    'xml-loader'
    ]
}

六 HTML 打包配置

webpack 处理 html 页面,一般是使用 html-webpack-plugin 插件。

// 安装
npm install html-webpack-plugin --save-dev

简单配置示例

const HtmlWebPackPlugin=require('html-webpack-plugin');

module.exports={
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    plugins: [new HtmlWebPackPlugin()]
    // 指定打包后页面的名称(默认index.html)和 title
    // plugins: [new HtmlWebPackPlugin({title: 'hello', filename: 'foo.html'})]
};

打包后会生成 index.html,内容如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Webpack App</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    </head>

    <body>
        <script src="main.js"></script>
    </body>
</html>

指定Template打包

指定一个 template,使打包后的 html 页面是依据这个 template 来打包的,这样 html 的内容我们可以自定义,比如我们需要打包后的文件有<div id="app"></div>节点。

指定 template:

<!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>Webpack</title>
    </head>
    <body>
        <h1>hello world</h1>
        <div id="app"></div>
    </body>
</html>

webpack.config.js 配置:

const HtmlWebPackPlugin=require('html-webpack-plugin');

module.exports={
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    plugins: [
        new HtmlWebPackPlugin({
            template: './src/index.html' // 指定模板
        })
    ]
};

打包后的 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>Webpack</title>
</head>
<body>
    <h1>hello world</h1>
    <div id="app"></div>
<script src="main.js"></script></body>
</html>

多页配置

单一入口:

const HtmlWebPackPlugin=require('html-webpack-plugin');

const indexPage=new HtmlWebPackPlugin({
    template: './src/index.html',
    filename: 'index.html'
});
const listPage=new HtmlWebPackPlugin({
    template: './src/list.html',
    filename: 'list.html'
});

module.exports={
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    plugins: [indexPage, listPage]
};

多入口:

const HtmlWebPackPlugin=require('html-webpack-plugin');

module.exports={
    mode: 'development',
    entry: {
        index: './src/index.js',
        list: './src/list.js'
    },
    plugins: [
        new HtmlWebPackPlugin({template: './src/index.html', filename: 'index.html', chunks: ['index']}),
        new HtmlWebPackPlugin({template: './src/list.html', filename: 'list.html', chunks: ['list']})
    ]
};

最佳实践:

/**
 * 1. /scripts/utils/index.js 
 * 使用 npm 库 globby 读取src/pages/*.js, 生成入口 entry
 */
const path=require('path');
const globby=require('globby');

const getEntry=(exports.getEntry=()=> {
    // 异步方式获取所有的路径
    const paths=globby.sync('./pages/*.js', {
        cwd: path.join(__dirname, './src')
    });
    const rs={};
    paths.forEach(v=> {
        // 计算 filename
        const name=path.basename(v, '.js');
        let p=path.join('./src', v);
        if (!p.startsWith('.')) {
            // 转成相对地址
            p='./' + p;
        }

        rs[name]=p;
    });
    return rs;
});

console.log(getEntry());

/**
 * 2. /scripts/utils/index.js
 * 遍历 entry,生成 html-webpack-plugins 数组
 */
const HtmlWebPackPlugin=require('html-webpack-plugin');

exports.getHtmlWebpackPlugins=()=> {
    const entries=getEntry();
    return Object.keys(entries).reduce((plugins, filename)=> {
        plugins.push(
            new HtmlWebPackPlugin({
                template: entries[filename],
                filename: `${filename}.html`,
                chunks: [filename]
            })
        );
        return plugins;
    }, []);
};

/**
 * 3. webpack.config.js
 * require 封装好的html-webpack-plugins 数组
 */

const {getEntry, getHtmlWebpackPlugins}=require('./scripts/utils');

module.exports={
    mode: 'development',
    getEntry(),
    plugins: [
        ...getHtmlWebpackPlugins()
    ]
};

七 Webpack Dev Server 本地开发服务

webpack-dev-server 是一个可以用来启动本地服务的插件。通过命令启动对应的服务。

// 安装
npm install webpack-dev-server
// 启动命令
npx webpack-dev-server

/* 命令常用参数 */
// 修改端口号和 host
webpack-dev-server --port 3000 --host 127.0.0.1
// 启动inline 模式的自动刷新
webpack-dev-server --hot --inline
// 手动指定 webpack config 文件
webpack-dev-server --config webpack.xxx.js
// 指定 webpack 的 mode
webpack-dev-server --mode development
// watch 功能,文件发生变化则触发重新编译
webpack-dev-server --watch
// dev-server默认会将工作目录(当前目录)作为基本目录,可以手动修改它
webpack-dev-server --content-base ./build

执行 webpack-dev-server 命令之后,它会读取 Webpack 的配置文件(默认是 webpack.config.js),然后将文件打包到内存中(所以看不到dist文件夹的生产,Webpack 会打包到硬盘上),这时候打开 server 的默认地址:localhost:8080 就可以看到文件目录或者页面(默认是显示 index.html,没有则显示目录)。

一般在项目开发中,启动本地服务的命令会配置在 package.json 的 scripts 属性中:

{
    "scripts": {
        "dev": "webpack-dev-server --mode development --config webpack.config.dev.js --hot --inline --port 3000"
    }
}

更多的时候,是通过 devServer 属性直接配置在 webpack.config.js 中:

const path=require('path');
module.exports={
    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        port: 9000, // 端口
        hot: true, // 热部署,开启热部署需要引入 HotModuleReplacementPlugin 插件
        inline: true,
        // proxy解决本地开发跨域的问题,/api所有请求都转发到了baidu.com
        proxy: {
            '/api': 'http://baidu.com'
            // secure: false // 支持 https
        }
        // 加载所有内部中间件之前和之后可以插入自定义的中间件
        before(app, server) {
            app.get('/api/mock.json', (req, res)=> {
                res.json({hello: 'world'});
            });
        },
        after(){}

    },
    plugins: [
        // 添加 hmr plugin
        new webpack.HotModuleReplacementPlugin()
    ]
};

HMR 即模块热替换(Hot Module Replacement)的简称,它可以在应用运行的时候,不需要刷新页面,就可以直接替换、增删模块。

八 注意事项

开发环境和生产环境的配置可能不同,需要分开、判断处理。

开发 vue 一般通过集成了 webpack 的 vue-cli 创建项目,但如果直接使用 webpack 需要引入 vue-loader。

作者:越谷
链接:https://juejin.cn/post/7380262523852455963
来源:稀土掘金

TOC

1.认识webpack

  • webpack是一个现代的JavaScript应用的静态模块打包工具
  • 1.模块化
  • 在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发。
  • 并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。 而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。
  • 而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做


  • 2.打包
  • 将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)
  • 并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。


  • 3.和grunt/gulp的对比
  • 如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。
  • 只需要进行简单的合并、压缩,就使用grunt/gulp即可。
  • 但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack

在这里插入图片描述



2.webpack的安装

  • 安装node.js(https://nodejs.org/zh-cn/)
  • 查看node版本 node -v
  • 全局安装webpack npm install webpack@3.6.0 -g,指定了版本为3.6.0
  • 局部安装webpack cd 对应目录;npm install webpack@3.6.0 --save-dev
  • 其中--save-dev是开发时依赖,项目打包后不需要继续使用
  • 为什么全局安装后,还需要局部安装呢?
  • 在终端直接执行webpack命令,使用的全局安装的webpack
  • 当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack


3.webpack的起步

  • 创建目录结构

在这里插入图片描述


  • 文件和文件夹解析:
  • dist文件夹:用于存放之后打包的文件
  • src文件夹:用于存放我们写的源文件
  • main.js:项目的入口文件。
  • mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。
  • info.js:定义了一些变量信息


  • index.html:浏览器打开展示的首页html
  • package.json:通过npm init生成的,npm包管理的文件


  • mathUtils.js function add(num1,num2){ return num1 + num2; } function mul(num1,num2){ return num1*num2; } module.exports={ add, mul }
  • info.js javascript<br />export const name='why';<br />export const height=180;<br />
  • main.js const {add,mul}=require('./mathUtils.js'); console.log(add(20,30)); console.log(mul(20,30)); import {name,height} from './info.js'; console.log(name); console.log(height); 注:可以看到main.js引入了其它js文件
  • webpack打包 webpack ./src/main.js ./dist/bundle.js
  • 打包后会在dist文件下,生成一个bundle.js文件
  • bundle.js文件,是webpack处理了项目直接文件依赖后生成的一个js文件,我们只需要将这个js文件在index.html中引入即可
  • index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="dist/bundle.js"></script> </body> </html>

4.webpack的配置

  • 目标:简化上面的打包命令
  • 1.创建webpack.config.js文件 const path=require('path') module.exports={ entry : "./src/main.js", output: { path: path.resolve(__dirname,'dist'), //动态获取绝对路径,__dirname是node中全局变量 filename : "bundle.js" }, }
  • 2.npm init,生成package.json文件,这里面是针对当前项目的主要描述文件(里面会生成所有安装的依赖)

在这里插入图片描述


  • 3.局部安装webpack----cd 当前目录;npm install webpack@3.6.0 --save-dev,生成node模块

在这里插入图片描述


  • 4.配置package.json文件,简化命令

在这里插入图片描述


  • 5.命令npm run build

5.loader的使用

  • 在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等
  • 对于webpack本身的能力来说,对于这些转化是不支持的。需要webpack扩展对应的loader
  • loader使用过程:
  • 步骤一:通过npm安装需要使用的loader
  • 步骤二:在webpack.config.js中的modules关键字下进行配置


5.1 css的loader使用

  • 1.创建css文件

在这里插入图片描述

css<br />body {<br /> background-color:red; <br />}<br />

  • 2.main.js引用 //1.使用commonjs的模块化规范 const {add,mul}=require('./js/mathUtils.js'); console.log(add(20,30)); console.log(mul(20,30)); //2.使用ES6的模块化规范 import {name,height} from './js/info.js'; console.log(name); console.log(height); //3.依赖css文件 require("./css/normal.css")
  • 3.run build打包(报错)

在这里插入图片描述


  • 4.https://v4.webpack.docschina.org/loaders/,配置cssloader文档
  • 5.npm install --save-dev css-loader@2.0.2
  • 6.npm install style-loader@0.23.1 --save-dev
  • 7.配置webpack.json.js文件

在这里插入图片描述


  • 8.index.html的使用 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> 你好 <h2> 你好 </h2> <script src="dist/bundle.js"></script> </body> </html>

5.2 less的loader使用(同理)

  • 1.创建less文件

在这里插入图片描述

@fontSize: 50px; @fontColor: orange; body{ font-size: @fontSize; color: @fontColor; }

  • 2.引入less文件(main.js) //1.使用commonjs的模块化规范 const {add,mul}=require('./js/mathUtils.js'); console.log(add(20,30)); console.log(mul(20,30)); //2.使用ES6的模块化规范 import {name,height} from './js/info.js'; console.log(name); console.log(height); //3.依赖css文件 require("./css/normal.css") //4.依赖less文件 require("./css/special.less")
  • 3.npm install less-loader@4.1.0 --save-devnpm install less@3.9.0 --save-dev
  • 4.配置webpack.json.js文件

在这里插入图片描述


  • 5.打包 run build

5.3 图片文件处理

  • 1.创建img文件

在这里插入图片描述


  • 2.修改css文件 body { /* background-color:red; */ background-image: url("../img/timg.jpg"); }
  • 3.直接打包会出错,需要安装url-loader包 npm install url-loader@1.1.2 --save-dev
  • 4.修改webpack.json.js文件

在这里插入图片描述


  • 5.这时候打包会成功,但是换了一张的图片就会报错

在这里插入图片描述


  • 6、安装file-loader处理大文件npm install file-loader@3.0.1 --save-dev
  • 7、再次打包即可成功

在这里插入图片描述

注:可以发现,对于大的图片打包成功会输出到dist文件夹下,此时引用的话会找不到,需要配置下路径

  • 8.修改webpack.config.js

在这里插入图片描述


  • 9.另外对于生成的图片名字太长,我们可以配置下生成规则--webpack.config.js,表示生成名字+8位哈希值+扩展名

在这里插入图片描述


5.4 babel的使用(es6转es5)

  • 1.npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
  • 2.配置webpack.json.js文件

在这里插入图片描述


  • run build

6.webpack中配置Vue

6.1 引入vue.js

  • 1 . npm install vue@2.5.21 --save 因为我们后续是在实际项目中也会使用vue的,所以并不是开发时依赖
  • 2 . main.js引入 //1.使用commonjs的模块化规范 const {add,mul}=require('./js/mathUtils.js'); console.log(add(20,30)); console.log(mul(20,30)); //2.使用ES6的模块化规范 import {name,height} from './js/info.js'; console.log(name); console.log(height); //3.依赖css文件 require("./css/normal.css") //4.依赖less文件 require("./css/special.less") //5.使用Vue进行开发 import Vue from "vue" new Vue({ el:'#app', data:{ name : 'codewhy' } })
  • 3.run build(报错)---这个错误说的是我们使用的是runtime-only版本的Vue,

在这里插入图片描述


  • 4.修改webpack.config.js配置,即可成功

在这里插入图片描述


  • 5.index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body > <div id="app"> 你好 </div> <h2> 你好 </h2> <script src="dist/bundle.js"></script> </body> </html>

6.2 el和template的区别

  • 正常运行之后,我们来考虑另外一个问题:
  • 如果我们希望将data中的数据显示在界面中,就必须是修改index.html
  • 如果我们后面自定义了组件,也必须修改index.html来使用组件
  • 但是html模板在之后的开发中,我并不希望手动的来频繁修改,是否可以做到呢?


  • 定义template属性:
  • 在前面的Vue实例中,我们定义了el属性,用于和index.html中的#app进行绑定,让Vue实例之后可以管理它其中的内容
  • 这里,我们可以将div元素中的{{message}}内容删掉,只保留一个基本的id为div的元素
  • 但是如果我依然希望在其中显示{{message}}的内容,应该怎么处理呢?
  • 我们可以再定义一个template属性,代码如下:

在这里插入图片描述



  • 那么,el和template模板的关系是什么呢?
  • 在我们之前的学习中,我们知道el用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等。
  • 而如果Vue实例中同时指定了template,那么template模板的内容会替换掉挂载的对应el的模板。


  • 这样做有什么好处呢?
  • 这样做之后我们就不需要在以后的开发中再次操作index.html,只需要在template中写入对应的标签即可


6.3 分离template模板

  • 书写template模块非常麻烦怎么办呢?
  • 没有关系,稍后我们会将template模板中的内容进行抽离。
  • 会分成三部分书写:template、script、style,结构变得非常清晰。

在这里插入图片描述



  • 1 .App.vue文件 <template> <div> <h2 class="title">{{message}}</h2> <button @click="btnClick">按钮</button> <h2>{{name}}</h2> <Cpn/> </div> </template> <script> import Cpn from "./Cpn.vue" export default { name:"App", components: { Cpn }, data(){ return { name : 'codewhy', message:"123" } }, methods:{ btnClick(){ } } } </script> <style scoped> .title{ color:green; } </style>
  • 2 .子组件Cpn.vue文件 <template> <div> <h2 class="title">我是cpn组件的标题</h2> <p>我是cpn组件的内容</p> <h2>{{name}}</h2> </div> </template> <script> export default { name:"Cpn", data(){ return { name : 'Cpn的组件的name', } }, methods:{ btnClick(){ } } } </script> <style scoped> .title{ color:green; } </style>
  • 3 .main.js引入App.vue //1.使用commonjs的模块化规范 const {add,mul}=require('./js/mathUtils.js'); console.log(add(20,30)); console.log(mul(20,30)); //2.使用ES6的模块化规范 import {name,height} from './js/info.js'; console.log(name); console.log(height); //3.依赖css文件 require("./css/normal.css") //4.依赖less文件 require("./css/special.less") //5.使用Vue进行开发 import Vue from "vue" // import App from "./vue/app" import App from './vue/App.vue' const app=new Vue({ el:'#app', // 如果同时有el和template,会直接用template 替换掉el内容 template: <App/> , components:{ App } })
  • 4 .index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body > <div id="app"> 你好 </div> <h2> 你好 </h2> <script src="dist/bundle.js"></script> </body> </html>

7.认识plugin

7.1 添加版权声明

  • 在webpack.config.js配置如下

在这里插入图片描述


  • 重新打包

在这里插入图片描述


7.2 HtmlWebpackPlugin

  • 目的:将index.html打包到dist中
  • 1.安装插件npm install html-webpack-plugin@3.2.0 --sava-dev
  • 2.在webpack.config.js导入

在这里插入图片描述


  • 3.因为index.html的js 打包后会自动引入,所以注释了dist 以及下面的内容 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body > <div id="app"> 你好 </div> <!-- <script src="dist/bundle.js"></script> --> </body> </html>
  • 4.最后生成的如下

在这里插入图片描述


7.3 压缩js的插件

  • 1.npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
  • 2.配置webpack.config.js

在这里插入图片描述


  • 3.npm run build
  • 对js丑化

在这里插入图片描述



8.plugin的使用搭建本地服务器

  • webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。
  • 不过它是一个单独的模块,在webpack中使用之前需要先安装它
  • npm install --save-dev webpack-dev-server@2.9.1


  • 修改webpack.config.js
  • devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:
  • contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
  • port:端口号
  • inline:页面实时刷新
  • historyApiFallback:在SPA页面中,依赖HTML5的history模式

在这里插入图片描述


  • 修改package.json文件

在这里插入图片描述


  • 开始热部署npm run dev

在这里插入图片描述

这样就会实现,当文件改变的时候,页面会自动修改,因为此时这些修改保存到了内存中,类似于java的热部署

  • 补充1:(自动打开链接)

在这里插入图片描述


  • 补充2:js压缩在开发中,不应该使用

在这里插入图片描述


9.webpack的配置分离

  • 目的:开发时,使用一个配置文件;发布时使用另一个配置文件
  • npm install webpack-merge@4.1.5 --save-dev 安装merge包,用于合并下面的配置文件
  • 所以将webpack.config.js抽离成3个文件
  • 1 . base.config.js const path=require('path') const webpack=require('webpack') const HtmlWebpackPlugin=require('html-webpack-plugin') const uglifyjsWebpackPlugin=require('uglifyjs-Webpack-plugin') module.exports={ entry : "./src/main.js", output: { path: path.resolve(__dirname,'../dist'), //动态获取绝对路径,__dirname是node中全局变量 filename : "bundle.js", //publicPath: 'dist/' }, plugins:[ new webpack.BannerPlugin('最终版权swz所有!'), new HtmlWebpackPlugin({ template:'index.html' }), //new uglifyjsWebpackPlugin() ], module: { rules: [ { test: /\.css/,//style¨E45Eloader将模块的导出作为样式添加到DOM中//css¨E45Eloader解析CSS文件后,使用import加载,并且返回CSS代码//使用多个loader时,是从右向左use:¨E91E′style¨E45Eloader′,′css¨E45Eloader′¨E93E¨E125E,¨E123Etest:/¨E92E.less/, // style-loader 将模块的导出作为样式添加到 DOM 中 // css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码 //使用多个loader时,是从右向左 use: ['style-loader','css-loader'] }, { test: /\.less/,//style¨E45Eloader将模块的导出作为样式添加到DOM中//css¨E45Eloader解析CSS文件后,使用import加载,并且返回CSS代码//使用多个loader时,是从右向左use:¨E91E′style¨E45Eloader′,′css¨E45Eloader′¨E93E¨E125E,¨E123Etest:/¨E92E.less/, use: [{ loader: 'style-loader' // creates style nodes from JS strings }, { loader: 'css-loader' // translates CSS into CommonJS }, { loader: 'less-loader' // compiles Less to CSS }] }, { test: /\.(png|jpg|jpeg|gif)/i,use:¨E91E¨E123Eloader:′url¨E45Eloader′,options:¨E123E//当加载的图片,小于limit时,会将图片编译成base64字符串形式//反之,需要使用file¨E45Eloader没模块进行加载limit:13000,//¨E91E¨E93E表示变量name:′img/¨E91Ename¨E93E.¨E91Ehash:8¨E93E.¨E91Eext¨E93E′¨E125E¨E125E¨E93E¨E125E,¨E123Etest:/¨E92E.m?js/i, use: [ { loader: 'url-loader', options: { //当加载的图片,小于limit时,会将图片编译成base64字符串形式 //反之,需要使用file-loader没模块进行加载 limit: 13000, //[]表示变量 name: 'img/[name].[hash:8].[ext]' } } ] }, { test: /\.m?js/i,use:¨E91E¨E123Eloader:′url¨E45Eloader′,options:¨E123E//当加载的图片,小于limit时,会将图片编译成base64字符串形式//反之,需要使用file¨E45Eloader没模块进行加载limit:13000,//¨E91E¨E93E表示变量name:′img/¨E91Ename¨E93E.¨E91Ehash:8¨E93E.¨E91Eext¨E93E′¨E125E¨E125E¨E93E¨E125E,¨E123Etest:/¨E92E.m?j exclude: , : { : , : { : [] } } }, { : s/,//exclude 排除,include包含/(node_modules|bower_components)/useloader'babel-loader'optionspresets'es2015'test/\.vue/,use:¨E91E′vue¨E45Eloader′¨E93E¨E125E¨E93E¨E125E,resolve:¨E123Ealias:¨E123E′vue/, : [] } ] }, :{ : { use'vue-loader'resolvealias'vue/,use:¨E91E′vue¨E45Eloader′¨E93E¨E125E¨E93E¨E125E,resolve:¨E123Ealias:¨E123E′vu: } }} e'"vue/dist/vue.esm.js"
  • 2 . prod.config.js const uglifyjsWebpackPlugin=require('uglifyjs-Webpack-plugin') const webpackMerge=require('webpack-merge') const baseConfig=require('./base.config') module.exports=webpackMerge(baseConfig, {plugins:[ new uglifyjsWebpackPlugin() ] })
  • 3 . dev.config.js const webpackMerge=require('webpack-merge') const baseConfig=require('./base.config') module.exports=webpackMerge(baseConfig, { devServer:{ contentBase: "./dist", inline: true } })
  • 修改配置文件package.json

在这里插入图片描述

注:以上安装的模块的版本要对应!!!

学习视频: https://www.bilibili.com/video/BV15741177Eh?p=90&spm_id_from=pageDriver

关概念

前端模块管理器(package management)

Web应用的目前的开发是各个模块组合起来(如:加载各种js插件),为了使用各个模块,需要使用大量的script标签引进模块,这样就造成了Web页面比较臃肿,同时在浏览器调用时也需要发送大量的HTTP请求.

为了解决这一问题,那么就需要使用前端模块管理器来管理JavaScript脚本的依赖关系,自动加载各个模块,使得网页结构清晰合理.

常用的前端模块管理器:Bower,Browserify,Component,Duo

WebPack

安装使用

使用说明

Switch to the directory containing webpack.config.js and run:

ebpack for building once for development

ebpack -p for building once for production (minification)

ebpack --watch for continuous incremental build in development (fast!)

webpack -d to include source maps

简单使用示例

  1. 项目初始化和模块安装

    npm init -y

    npm install jquery --save

    npm install webpack --save-dev

  2. 在项目根目录下创建webpack的配置文件webpack.config.js

    var webpack=require('webpack');

    var commonsPlugin=new webpack.optimize.CommonsChunkPlugin('common.js');

    module.exports={

    //插件项

    plugins: [commonsPlugin],

    //页面入口文件配置

    entry: './src',

    //入口文件输出配置

    output: {

    path: 'builds',

    filename: 'bundle.js'

    },

    };

  3. 相关代码文件

    根目录下创建src和builds

    app的入口文件(ES5语法):

    src/index.js

    var $=require('jquery');

    $('body').html('Hello');

    项目根目录下创建index.html

    <!DOCTYPE html>

    <html>

    <head></head>

    <body>

    <h1>My title</h1>

    <a>Click me</a>

    <script src="builds/common.js"></script>

    <script src="builds/bundle.js"></script>

    </body>

    </html>

  4. 运行WebPack命令编译bundle.js

    执行命令node_modules\.bin\webpack后浏览器查看效果

将Bootstrap示例代码使用WebPack打包

安装所需的加载器

npm install css-loader node-sass resolve-url-loader sass-loader style-loader url-loader file-loader --save

解决jQuery全局问题

npm install jquery --save-dev

WebPack配置文件

var webpack=require('webpack');

var commonsPlugin=new webpack.optimize.CommonsChunkPlugin('common.js');

module.exports={

//插件项

plugins: [commonsPlugin],

//页面入口文件配置

entry: './src/main.js',

//入口文件输出配置

output: {

path: 'builds',

filename: 'bundle.js'

},

module: {

//加载器配置

loaders: [

{

// 得到jquery模块的绝对路径

test: require.resolve('jquery'),

// 将jquery绑定为window.jQuery

loader: 'expose?jQuery'

},

// 匹配各种资源文件

{

test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,

loader: 'url-loader?limit=50000&name=[path][name].[ext]'

},

{

test: /\.js/,

loader: 'babel',

},

{

test: /\.css$/,

loader: 'style!css'

},

{

test: /\.html/,

loader: 'html',

}

]

},

plugins:[

new webpack.ProvidePlugin({

// jQuery设置为全局变量

$:"jquery",

jQuery:"jquery",

"window.jQuery":"jquery"

})

]

};

入口文件main.js

// 加载css资源

require('./css/bootstrap.min.css');

require('./css/bootstrap-theme.min.css');

require('./css/theme.css');

// 加载其他js资源

//require('jquery');

require('./js/bootstrap.min.js');

require('./js/docs.min.js');

require('./js/ie10-viewport-bug-workaround.js');

这里是使用打包bootstrap源码的方式,也可以使用npm install bootstrap --save的方式加载bootstrap

打包运行与原生bootstrap的比较网络传输速度

测试效果图

注意:

  1. 测试前为了防止浏览器缓存需要清除浏览器缓存

  2. 如果静态资源文件有些会经常变化,可采用分文件打包的方式进行打包

参考文章

前端模块管理器简介

webpack入门——webpack功能集合的demo

基于webpack搭建前端工程解决方案探索

webpack中引用jquery的简单实现