前端工程化之webpack4.0

·  阅读 117

概要

随着前端的不断发展与壮大,前端变得越来越复杂,组件化、模块化、工程化、自动化成了前端发展中不可或缺的一部分,具体到前端工程化,面临的问题是如何提高编码->测试->维护阶段的生产效率。

前端工程化是使用软件工程的技术和方法来进行前端项目的开发、维护和管理。

前端工程化的任务

前端不仅要保证功能还要考虑性能,要减少http请求数量、压缩、合并、预处理、规范代码、清理、打包、转换等工作。

前端大部分情况下源代码无法直接运行,必须通过转换后才可以正常运行。构建就是做这件事情,把源代码转换成发布到线上的可执行 JavaScrip、CSS、HTML 代码,包括如下内容。

  1. 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等;
  2. 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等;
  3. 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载;
  4. 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件;
  5. 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器;
  6. 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过;
  7. 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

构建其实是工程化、自动化思想在前端开发中的体现,把一系列流程用代码去实现,让代码自动化地执行这一系列复杂的流程。 构建给前端开发注入了更大的活力,解放了我们的生产力。

前端工程化工具

历史上先后出现一系列构建工具,它们各有其优缺点。由于前端工程师很熟悉 JavaScript ,Node.js 又可以胜任所有构建需求,所以大多数构建工具都是用 Node.js 开发的。

构建工具的主要功能就是实现自动化处理,例如对代码进行检查、预编译、合并、压缩;生成雪碧图、sourceMap、版本管理;运行单元测试、监控等,当然有的工具还提供模块化、组件化的开发流程功能。

如果把工具按类型分可以分为这三类:

基于任务运行的工具:Grunt、Gulp

它们会自动执行指定的任务,就像流水线,把资源放上去然后通过不同插件进行加工,它们包含活跃的社区,丰富的插件,能方便的打造各种工作流。

基于模块化打包的工具:Browserify、Webpack、rollup.js

有过 Node.js 开发经历的应该对模块很熟悉,需要引用组件直接一个 require 就 OK,这类工具就是这个模式,还可以实现按需加载、异步加载模块。

整合型工具:Yeoman、FIS、jdf、Athena、cooking、weflow

使用了多种技术栈实现的脚手架工具,好处是即开即用,缺点就是它们约束了技术选型,并且学习成本相对较高。

WebPack

webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

Webpack 是一个打包模块化 JavaScript 的工具,在 Webpack 里一切文件皆模块,通过 Loader 转换文件,通过 Plugin 注入钩子,最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。

其官网的首页图很形象的画出了 Webpack 是什么,如下:

一切文件:JavaScript、CSS、SCSS、图片、模板,在 Webpack 眼中都是一个个模块,这样的好处是能清晰的描述出各个模块之间的依赖关系,以方便 Webpack 对模块进行组合和打包。经过Webpack的处理,最终会输出浏览器能使用的静态资源。

Webpack 具有很大的灵活性,能配置如何处理文件,大致使用如下:

module.exports = {
// 所有模块的入口,Webpack 从入口开始递归解析出所有依赖的模块
    entry: './app.js',
    output: {
// 把入口所依赖的所有模块打包成一个文件 bundle.js 输出 
        filename: 'bundle.js'
    }
}
复制代码

Webpack的特点

把一切都视为模块:不管是 CSS、JS、Image 还是 HTML 都可以互相引用,通过定义 entry.js,对所有依赖的文件进行跟踪,将各个模块通过 loader 和 plugins 处理,然后打包在一起。

按需加载:打包过程中 Webpack 通过 Code Splitting 功能将文件分为多个 chunks,还可以将重复的部分单独提取出来作为 commonChunk,从而实现按需加载。

Webpack 特别适合配合 React.js、Vue.js 构建单页面应用以及需要多人合作的大型项目,在规范流程都已约定好的情况下往往能极大的提升开发效率与开发体验。

资源

官网:webpack.js.org/

github:github.com/webpack/web…

中文网:www.webpackjs.com/

深入浅出webpack电子书:webpack.wuhaolin.cn/

工作流程

搭建WebPack开发环境

  1. 安装NodeJS,去nodejs.cn/download/下载…

2. 安装webpack: npm init -y yarn add webpack webpack-cli --dev 3. 创建配置文件webpack.config.js ```JavaScript //webpack配置文件 //依赖node中的path模块 const path=require('path');

//定义一个默认模块对象
module.exports={
    //指定入口文件的位置
    entry:"./src/index.js",
    //设置输出结果
    output: {
        //路径,将相对路径转绝对路径
        path:path.resolve(__dirname,'dist'),
        //文件
        filename: "bundle.js"
    }
};
```
path.resolve获取文件的路径,_dirname为当前模块的绝对路径,详细解释如下:
复制代码
  1. 使用webpack命令打包。
    webpack --config webpack.config.js
    复制代码

核心概念与执行过程

在Webpack里一切皆模块,一个模块对应着一个文件。Webpack会从配置的Entry开始递归找出所有依赖的模块。

webpack把一切都视为模块,不管是 CSS、JS、Image 还是 HTML 都可以互相引用。Node.js 从最一开始就支持模块化编程。在web存在多种支持JavaScript模块化的工具,这些工具各有优势和限制。webpack基于从这些系统获得的经验教训,并将模块的概念应用于项目中的任何文件。

什么是 webpack 模块
对比 Node.js 模块,webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:

  1. ES2015 import 语句;
  2. CommonJS require() 语句;
  3. AMD define 和 require 语句;
  4. css/sass/less 文件中的 @import 语句;
  5. 样式(url(...))或 HTML 文件(<img src=...>)中的图片链接(image url)。

支持的模块类型

webpack 通过 loader 可以支持各种语言和预处理器编写模块。loader 描述了 webpack 如何处理 非 JavaScript(non-JavaScript) 模块,并且在 bundle 中引入这些依赖。 webpack 社区已经为各种流行语言和语言处理器构建了 loader,包括:

  1. CoffeeScript
  2. TypeScript
  3. ESNext (Babel)
  4. Sass
  5. Less
  6. Stylus

2.1 入口

入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

每个依赖项随即被处理,最后输出到称之为 bundles 的文件中,我们将在下一章节详细讨论这个过程。

可以通过在 webpack 配置中配置 entry 属性,来指定一个入口起点(或多个入口起点)。默认值为 ./src。

接下来我们看一个 entry 配置的最简单例子:

//webpack.config.js

module.exports = {
    entry: './path/to/my/entry/file.js'
};
复制代码

根据应用程序的特定需求,可以以多种方式配置 entry 属性。

{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist'
  }
}
复制代码

常用的占位:

  1. [hash]:模块标识符(module identifier)的 hash;
  2. [chunkhash]:chunk 内容的 hash;
  3. [name]:模块名称,key的名称,非文件名称;
  4. [id]:模块标识符(module identifier);
  5. [query]:模块的 query,例如,文件名 ? 后面的字符串;
  6. [contenthash]:单个文件的hash。

2.2 出口(output)

输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。
output 属性告诉 webpack在哪里输出它所创建的bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

//webpack.config.js
const path = require('path');
module.exports = {
    entry: './path/to/my/entry/file.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'my-first-webpack.bundle.js'
    }
};
复制代码

在上面的示例中,我们通过 output.filename 和 output.path 属性,来告诉 webpack bundle 的名称,以及我们想要 bundle 生成(emit)到哪里。可能你想要了解在代码最上面导入的 path 模块是什么,它是一个 Node.js 核心模块,用于操作文件路径。

2.3 配置打包的 mode

我们需要在打包时提供mode属性来区分是开发环境还是生产环境,来实现配置文件的拆分

├── build
│   ├── webpack.base.js
│   ├── webpack.dev.js
│   └── webpack.prod.js
复制代码

可以通过 config 参数指定,使用哪个配置文件来进行打包,通过 env 参数区分生产环境和开发环境。

在package.json中配置打包命令:

"scripts": {
    "build": "webpack --env.production --config ./build/webpack.base",
    "dev": "webpack --env.development --config ./build/webpack.base"
}
复制代码

合并配置文件

我们可以判断当前环境是否是开发环境来加载不同的配置,这里我们需要做配置合并
安装webpack-merge:

npm install webpack-merge --save-dev
复制代码

webpack.dev配置

module.exports = {
  mode: "development",
};
复制代码

webpack.prod配置

module.exports = {
  mode: "production",
};
复制代码

webpack.base配置

const path = require("path");
const merge = require("webpack-merge");
// 开发环境
const dev = require("./webpack.dev");
// 生产环境
const prod = require("./webpack.prod");
const base = {
  // 基础配置
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "../dist"),
  },
};
module.exports = (env) => {
  if (env.development) {
    return merge(base, dev);
  } else {
    return merge(base, prod);
  }
};
复制代码

后续的开发中,我们会将公共的逻辑放到base中,开发和生产对的配置也分别进行存放!

2.4 模块转换器(loader)

模块转换器,用于把模块原内容按照需求转换成新内容。

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

在更高层面,在 webpack 的配置中 loader 有两个目标:

  1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
  2. use 属性,表示进行转换时,应该使用哪个 loader。
//webpack.config.js;
const path = require("path");
module.exports = {
  output: {
    filename: "my-first-webpack.bundle.js",
  },
  module: {
    rules: [{ test: /\.txt$/, use: "raw-loader" }],
  },
};
复制代码

以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 如下信息:

“webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先使用 raw-loader 转换一下。”

2.5 插件(plugins)

扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。

loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
module.exports = {
    module: {
        rules: [
            { test: /\.txt$/, use: 'raw-loader' }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({template: './src/index.html'})
    ]
};
复制代码

2.6 模式(mode)

通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化

module.exports = {
    mode: 'production'
};
复制代码

采用多个webpack文件时,可以将模式分别设置到生产和开发文件中。

webpack4允许我们指定编译使用开发模式还是生产模式,这由mode这个配置来控制,value为枚举值:development/production,分别对应开发模式和生产模式(这个配置可以作为命令行的配置参数也可以作为配置文件中的一个配置项,默认值是production,即生产模式)。

源码还是不支持调试(都用eval函数包住),指定编译时的source-map生成方式,默认值是eval,可以解决问题。

2.7 代码块(Chunk)

一个 Chunk 由多个模块组合而成,用于代码合并与分割。

2.8 WebPack执行过程

Webpack 启动后会从 Entry 里配置的 Module 开始递归解析 Entry 依赖的所有 Module。 每找到一个 Module, 就会根据配置的 Loader 去找出对应的转换规则,对 Module 进行转换后,再解析出当前 Module 依赖的 Module。 这些模块会以 Entry 为单位进行分组,一个 Entry 和其所有依赖的 Module 被分到一个组也就是一个 Chunk。最后 Webpack 会把所有 Chunk 转换成文件输出。 在整个流程中 Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。

模块转换器Loader

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。!

loader 是对应用程序中资源文件进行转换。它们是(运行在 Node.js中的)函数,可以将资源文件作为参数的来源,然后返回新的资源文件。

3.1 使用loader

在 webpack.config.js 文件中指定 loader,module.rules 允许你在 webpack 配置中指定多个 loader。 默认loader的顺序是从下到上从右向左执行,当然执行顺序也可以手动定义的。这是展示 loader 的一种简明方式,并且有助于使代码变得简洁。同时让你对各个 loader 有个全局概览:

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        { loader: "style-loader" },
        {
          loader: "css-loader",
          options: {
            modules: true,
          },
        },
      ],
    },
  ];
}
复制代码

Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置,Loaders的配置包括以下几方面:

test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
loader:loader的名称(必须)
include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
query:为loaders提供额外的设置选项(可选)
复制代码

3.2 常见的loader

3.2.1 文件

  • raw-loader 加载文件原始内容(utf-8)
  • val-loader 将代码作为模块执行,并将 exports 转为 JS 代码
  • url-loader 像 file loader 一样工作,但如果文件小于限制,可以返回 data URL
  • file-loader 将文件发送到输出文件夹,并返回(相对)URL

3.2.2 JSON

  • json-loader 加载 JSON 文件(默认包含)
  • json5-loader 加载和转译 JSON 5 文件
  • cson-loader 加载和转译 CSON 文件

3.2.3 转换编译(Transpiling)

  • script-loader 在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析
  • babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
  • buble-loader 使用 Bublé 加载 ES2015+ 代码,并且将代码转译为 ES5
  • traceur-loader 加载 ES2015+ 代码,然后使用 Traceur 转译为 ES5
  • ts-loader 或 awesome-typescript-loader 像 JavaScript 一样加载 TypeScript 2.0+
  • coffee-loader 像 JavaScript 一样加载 CoffeeScript

3.2.4 模板(Templating)

  • html-loader 导出 HTML 为字符串,需要引用静态资源
  • pug-loader 加载 Pug 模板并返回一个函数
  • jade-loader 加载 Jade 模板并返回一个函数
  • markdown-loader 将 Markdown 转译为 HTML
  • react-markdown-loader 使用 markdown-parse parser(解析器) 将 Markdown 编译为 - React 组件
  • posthtml-loader 使用 PostHTML 加载并转换 HTML 文件
  • handlebars-loader 将 Handlebars 转移为 HTML
  • markup-inline-loader 将内联的 SVG/MathML 文件转换为 HTML。在应用于图标字体,或将 CSS 动画应用于 SVG 时非常有用。

3.2.5 样式

  • style-loader 将模块的导出作为样式添加到 DOM 中
  • css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
  • less-loader 加载和转译 LESS 文件
  • sass-loader 加载和转译 SASS/SCSS 文件
  • postcss-loader 使用 PostCSS 加载和转译 CSS/SSS 文件
  • stylus-loader 加载和转译 Stylus 文件

3.2.6 清理和测试(Linting && Testing)

  • mocha-loader 使用 mocha 测试(浏览器/NodeJS)
  • eslint-loader PreLoader,使用 ESLint 清理代码
  • jshint-loader PreLoader,使用 JSHint 清理代码
  • jscs-loader PreLoader,使用 JSCS 检查代码样式
  • coverjs-loader PreLoader,使用 CoverJS 确定测试覆盖率

3.3 raw-loader(文件原始内容转换器)

一个可以用于加载文件作为字符串使用的加载器,使用UTF-8编码。
安装

yarn add raw-loader  --dev
复制代码
module: {
  rules: [
    {
      test: /\.txt$/,
      use: "raw-loader",
    },
  ];
}
复制代码

3.4 处理 CSS 文件

3.4.1 解析 css 样式

我们在js文件中引入 css 样式!

import "./index.css";
复制代码

再次执行打包时,会提示 css 无法解析

ERROR in ./src/index.css 1:4
Module parse failed: Unexpected token (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
复制代码

安装 loader

yarn add style-loader css-loader --save-dev
复制代码
module: {
  rules: [
    {
      test: /\.css$/,
      use: ["style-loader", "css-loader"],
    },
  ];
}
复制代码

3.4.2 抽离样式文件

默认只在打包时进行样式抽离

module.exports = (env) => {
  let isDev = env.development;
  const base = {
    /*source...*/
  };
  if (isDev) {
    return merge(base, dev);
  } else {
    return merge(base, prod);
  }
};
复制代码

安装抽离插件

npm install mini-css-extract-plugin --dev
复制代码

配置抽离插件

{
    test: /\.css$/,
    use: [
        !isDev && MiniCssExtractPlugin.loader,
        isDev && 'style-loader',
        "css-loader"
    ].filter(Boolean)
}
!isDev && new MiniCssExtractPlugin({
    filename: "css/[name].css"
})
复制代码

最终文件配置贴一下:

const path = require("path");
const dev = require("./webpack.dev");
const prod = require("./webpack.prod");
const merge = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = (env) => {
  let isDev = env.development;
  const base = {
    entry: "./src/index.js",
    output: {
      filename: "[name].js",
      path: path.resolve(__dirname, "../dist"),
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            !isDev && MiniCssExtractPlugin.loader,
            isDev && "style-loader",
            "css-loader",
          ].filter(Boolean),
        },
      ],
    },
    plugins: [
      !isDev &&
        new MiniCssExtractPlugin({
          filename: "css/[name].css",
        }),
      new HtmlWebpackPlugin({
        filename: "index.html",
        template: path.resolve(__dirname, "../public/template.html"),
        hash: true,
        minify: {
          removeAttributeQuotes: true,
        },
      }),
    ].filter(Boolean),
  };
  if (isDev) {
    return merge(base, dev);
  } else {
    return merge(base, prod);
  }
};
复制代码

3.4.3 css 预处理器

不同的 css 预处理器要安装不同的 loader 来进行解析

  • sass: sass-loader node-sass
  • less: less-loader less
  • stylus: stylus-loader stylus

使用sass

{
    test:/\.scss$/,
    use:[
        !isDev && MiniCssExtractPlugin.loader,
        isDev && 'style-loader',
        "css-loader",
        "sass-loader"
    ].filter(Boolean)
}
复制代码

在 css 文件中可能会使用@import语法引用css文件,被引用的css文件中可能还会导入scss

{
    test: /\.css$/,
    use: [
    !isDev && MiniCssExtractPlugin.loader,
    isDev && 'style-loader',
    {
        loader:"css-loader",
        options:{
            importLoaders:1 // 引入的文件需要调用sass-loader来处理
        }
    },
    "sass-loader"
    ].filter(Boolean)
},
复制代码

3.4.4 处理样式前缀

使用postcss-loader增加样式前缀

npm install postcss-loader autoprefixer
复制代码

在处理 css 前先增加前缀

 {
    test: /\.css$/,
    use: [
    !isDev && MiniCssExtractPlugin.loader,
    isDev && 'style-loader',
    {
        loader:"postcss-loader",
        options:{
            plugins:[require('autoprefixer')]
        }
    },
    "postcss-loader",
    "sass-loader"
    ].filter(Boolean)
},
复制代码

或者也可以创建postcss的配置文件postcss.config.js

module.exports = {
  plugins: [require("autoprefixer")],
};
复制代码

可以配置浏览器的兼容性范围 .browserslistrc

cover 99.5%
复制代码

3.4.5 css 压缩

在生产环境下我们需要压缩css文件,配置minimizer选项,安装压缩插件

npm i optimize-css-assets-webpack-plugin terser-webpack-plugin --save-dev
复制代码

webpack.prod.js文件中配置压缩

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserJSPlugin = require("terser-webpack-plugin");
optimization: {
  minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})];
}
复制代码

3.4.6 文件指纹

  • Hash 整个项目的 hash 值
  • chunkhash 根据入口产生 hash 值
  • contentHash 根据每个文件的内容产生的 hash 值

我们可以合理的使用hash戳,进行文件的缓存

!isDev &&
  new MiniCssExtractPlugin({
    filename: "css/[name].[contentHash].css",
  });
复制代码

3.5 处理文件类型

3.5.1 处理引用的图片

import logo from "./webpack.png";
let img = document.createElement("img");
img.src = logo;
document.body.appendChild(img);
复制代码

使用file-loader,会将图片进行打包,并将打包后的路径返回

{
    test:/\.jpe?g|png|gif/,
    use:{
        loader:'file-loader',
        options:{
            name:`img/[name].[ext]`
        }
    }
}
复制代码

3.5.2 处理 icon

二进制文件也是使用file-loader来打包

{
    test:/woff|ttf|eot|svg|otf/,
    use:{
        loader:'file-loader'
    }
}
复制代码

3.5.3 转化成 base64

使用url-loader将满足条件的图片转化成 base64,不满足条件的url-loader会自动调用file-loader来进行处理

{
    test:/\.jpe?g|png|gif/,
    use:{
        loader:'url-loader',
        options:{
            limit:100*1024,
            name:`img/[name].[ext]`
        }
    }
}
复制代码

3.6 处理 JS 模块

3.6.1 将es6代码编译成es5代码

代码的转化工作要交给babel来处理

yarn add @babel/core @babel/preset-env babel-loader --dev
复制代码

@babel/core是 babel 中的核心模块,@babel/preset-env 的作用是 es6 转化 es5 插件的插件集合,babel-loaderwebpackloader的桥梁。

const sum = (a, b) => {
  return a + b;
};
复制代码

增加babel的配置文件 .babelrc

{
  "presets": [["@babel/preset-env"]]
}
复制代码

配置 loader

module: {
	rules: [{ test: /\.js$/, use: "babel-loader" }]
},
复制代码

现在打包已经可以成功的将 es6 语法转化成 es5 语法!

3.6.2 解析装饰器

yarn add @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators --dev
复制代码
"plugins": [
  ["@babel/plugin-proposal-decorators", { "legacy": true }],
  ["@babel/plugin-proposal-class-properties",{"loose":true}]
]
复制代码

legacy:true表示继续使用装饰器装饰器,loose 为 false 时会采用Object.defineProperty定义属性

  • Plugin 会运行在 Preset 之前
  • Plugin 会从第一个开始顺序执行,Preset 则是相反的

3.6.3 polyfill

根据.browserslistrc文件,转化使用到的浏览器 api

"presets": [
    ["@babel/preset-env",{
        "useBuiltIns":"usage", // 按需加载
        "corejs":2 // corejs 替代了以前的pollyfill
    }]
]
复制代码

安装 corejs

yarn add core-js@2
复制代码

使用transform-runtime A plugin that enables the re-use of Babel's injected helper code to save on codesize.可以帮我们节省代码

yarn add @babel/plugin-transform-runtime @babel/runtime --dev
复制代码

.babelrc中配置插件

"plugins": [
    "@babel/plugin-transform-runtime"
]
复制代码

3.6.4 添加 eslint

安装eslint

npm install eslint
npx eslint --init # 初始化配置文件
复制代码
{
    test:/\.js/,
    enforce:'pre',
    use:'eslint-loader'
},
复制代码

配置eslint-loader可以实时校验 js 文件的正确性,pre表示在所有loader执行前执行

3.7 配置 TS 环境

3.7.1 使用 ts-loader

使用ts需要安装ts相关配置

npm install typescript ts-loader --save-dev
复制代码

生成ts的配置文件

npx tsc --init
复制代码

配置ts-loader

{
    test:/\.tsx?/,
    use: ['ts-loader'],
    exclude: /node_modules/
}
复制代码

将入口文件更改成ts文件

let a: string = "hello";
console.log(a);
复制代码

执行npm run dev发现已经可以正常的解析ts文件啦!

3.7.2 使用 preset-typescript

不需要借助typescript

npm install @babel/preset-typescript
复制代码
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": 2
      }
    ],

    "@babel/preset-react",

    [
      "@babel/preset-typescript",
      {
        "allExtensions": true
      }
    ]
  ],

  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose": true }],
    "@babel/plugin-transform-runtime"
  ]
}
复制代码

插件plugins

4.1 插件概要

Plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性。

插件是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!插件目的在于解决 loader 无法实现的其他事。

4.2 HTML Webpack Plugin(创建HTML插件)

该个插件的作用是用来自动生成html页面,既可以生成单个页面又可以生成多个页面,并且在生成前可以给它一些的配置参数,它会按照你想要的生成方式去生成页面。

安装

yarn add html-webpack-plugin --dev
复制代码

在webpack.config.js里引入模块

const HtmlWebpackPlugin=require('html-webpack-plugin');
复制代码

在webpack.config.js中的plugins对象里new一个实例

new HtmlWebpackPlugin({
  template: "./src/templates/tmpl03.html",
  templateParameters: {
    title: "Hello App",
    content: "123456",
    key: "value",
  },
  minify: {
    removeComments: true, //移除注释
  },
}),
复制代码

参数:

  • title:生成页面的titile元素;
  • filename:生成的html文件的文件名。默认index.html,可以直接配置带有子目录;
  • template:模版文件路径;
  • inject:插入的script插入的位置,四个可选值:
    • true: 默认值,script标签位于html文件的body底部;
    • body: 同true;
    • head: script标签位于html文件的head标签内;
    • false: 不插入生成的js文件,只是生成的html文件。
  • minify:对html文件进行压缩。属性值是false或者压缩选项值。默认false不对html文件进行压缩。
  • hash:给生成的js文件尾部添加一个hash值。这个hash值是本次webpack编译的hash值。默认false;
  • chunks:在html文件中引用哪些js文件,用于多入口文件时。不指定chunks时,所有文件都引用。

4.3 Mini-css-extract-plugin(单独提取CSS插件)

将CSS提取为独立的文件的插件,对每个包含css的js文件都会创建一个CSS文件,支持按需加载css和sourceMap。

默认情况下css是被js注入的一段style,如下所示:

安装:

yarn add mini-css-extract-plugin --dev
复制代码

使用

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // 类似 webpackOptions.output里面的配置 可以忽略
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            MiniCssExtractPlugin.loader,
            options: {
              // 这里可以指定一个 publicPath
              // 默认使用 webpackOptions.output中的publicPath
              publicPath: "../",
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
复制代码

这个插件应该只用在 production 配置中,并且在loaders链中不使用 style-loader, 特别是在开发中使用HMR,因为这个插件暂时不支持HMR

4.4 clean-webpack-plugin(删除或清理构建目录)

在用HtmlWebpackPlugin的时候时需要把dist目录删掉再去看生成的文件,clean-webpack-plugin这个插件就可以做这件事情

在用HtmlWebpackPlugin的时候时需要把dist目录删掉再去看生成的文件,clean-webpack-plugin这个插件就可以做这件事情

安装

yarn add clean-webpack-plugin --dev
复制代码

在webpack.config.js里引入模块

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
复制代码

在plugins的最前面创建清理对象

plugins:[
new CleanWebpackPlugin(['./dist']), //这个一定要放在最上面,作用是先删除dist目录再创建新的dist目录。里面的参数为要删除的目录,放在一个数组里面
...
]
复制代码

4.4.1 cleanOnceBeforeBuildPatterns

这个参数配置在构建前删除那些文件,和不要删除那些文件,不要删除的文件前面加个逻辑运算符非 ! ,*号可以通过站位符来处理,表示什么开头,什么结尾啥的

new CleanWebpackPlugin({
    cleanOnceBeforeBuildPatterns: ['main*.*', '!vendor', '!vendor.manifest.json']
}),
复制代码

4.4.2 cleanAfterEveryBuildPatterns

这个参数配置表示在构建后要删除那些文件,和不要删除那些文件,不要删除的文件前面加个逻辑运算符非 ! ,*号可以通过站位符来处理,表示什么开头,什么结尾啥的

new CleanWebpackPlugin({
    cleanAfterEveryBuildPatterns: ['static*.*', '!static1.js'],
}),
复制代码

关于clean-webpack-plugin插件的所有配置参数请参考:www.npmjs.com/package/cle…

DevServer开发服务器

webpack-dev-server就是一个基于Node.js和webpack的一个简易服务器。它在服务器端使用webpack-dev-middleware进行webpack构建打包;并在客户端注入一份runtime,用于接受服务器端的构建打包后信息。

5.1 快速开启DevServer

安装成功后在项目的根目录下执行webpack-dev-server 命令, DevServer 服务器就启动了,这时你会看到控制台有一串日志输出:

Project is running at http://localhost:8080/
webpack output is served from /
复制代码

这意味着 DevServer 启动的 HTTP 服务器监听在 http://localhost:8080/ ,DevServer 启动后会一直驻留在后台保持运行,访问这个网址你就能获取项目根目录下的 index.html。

注意: 并没有通过webpack命令生成一个dist目录,然后在浏览器里输入地址http://localhost:8080/后,页面会正常显示。这个原因是devServer会将webpack构建出的文件保存到内存里,不需要打包生成就能预览

5.2 参数设置

在webpack.config.js中可以根据需要配置dev-server满足你更多的需求。

// webpack.config.js 配置一下 devServer
devServer: {
    clientLogLevel: 'warning',
    historyApiFallback: true,
    hot: true,
    compress: true,
    host: 'localhost',
    port: 8080
  }
复制代码
  • Hot:热模块更新作用。即修改或模块后,保存会自动更新,页面不用刷新呈现最新的效果。

这不是和 webpack.HotModuleReplacementPlugin (HMR) 这个插件不是一样功能吗?是的,不过请注意了,HMR 这个插件是真正实现热模块更新的。而 devServer 里配置了 hot: true , webpack会自动添加 HMR 插件。所以模块热更新最终还是 HMR 这个插件起的作用;

  • host:写主机名的。默认 localhos;
  • port:端口号。默认 8080;
  • compress:如果为 true,开启虚拟服务器时,为你的代码进行压缩。加快开发流程和优化的作用;
  • contentBase:你要提供哪里的内容给虚拟服务器用。这里最好填绝对路径。默认情况下,它将使用您当前的工作目录来提供内容;
  • publicPath:配置了 publicPath后, url = '主机名' + 'publicPath配置的' +

'原来的url.path'。这个其实与 output.publicPath 用法大同小异;

  • proxy:当您有一个单独的API后端开发服务器,并且想要在同一个域上发送API请求时,则代理这些 url 。看例子好理解。
      proxy: {
    '/proxy': {
        target: 'http://your_api_server.com',
        changeOrigin: true,
        pathRewrite: {
            '^/proxy': ''
        }
    }
    复制代码
    1. 假设你主机名为 localhost:8080 , 请求 API 的 url 是 http://your_api_server.com/user/list;
    2. '/proxy':如果点击某个按钮,触发请求 API 事件,这时请求 url 是http://localhost:8080/proxy/user/list;
    3. changeOrigin:如果 true ,那么 http://localhost:8080/proxy/user/list 变为 http://your_api_server.com/proxy/user/list 。但还不是我们要的 url ;
    4. pathRewrite:重写路径。匹配 /proxy ,然后变为'' ,那么 url 最终为 http://your_api_server.com/user/list 。
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改