前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零搭建一个 webpack 脚手架工具(二)

从零搭建一个 webpack 脚手架工具(二)

作者头像
多云转晴
发布2019-12-16 11:38:10
1.4K0
发布2019-12-16 11:38:10
举报
文章被收录于专栏:webTowerwebTower

其他 loader 配置

配置完有关 CSS loader 后,还有一个问题,我们不想将 CSS 都插入到 style 标签中,如果 CSS 样式代码很多,会导致生成的 HTML 文件很大,我们希望使用 <link> 标签引入打包后的 CSS 文件(将 CSS 单独提取出来),这时候就要使用一个插件:mini-css-extract-plugin

下载:yarn add mini-css-extract-plugin -D

配置:

代码语言:javascript
复制
// webpack.config.dev.js
let MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    // ....

    plugins: [
        new MiniCssExtractPlugin({
            // 抽离的样式叫什么名字(会生成在 css 文件夹下)
            filename: "css/main.css",
        });
    ],

    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // 将 style-loader 替换掉(不再将 css 样式放在 style 标签中)
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            }
        ]
    }
}

eslint

eslint 是 JS 语法的校验器,它提供了一个 loader:eslint-loader。使用之前需要先下载:yarn add eslint eslint-loader,配置如下:

代码语言:javascript
复制
{
    rules: [
        {
            test: /\.js$/,
            use: {
                laoder: 'eslint-loader',
            }
        }
    ]
}

设置好 loader 后,还要在项目根目录下建一个 .eslintrc.json 文件再进行其他配置。

当然,也可以来到这个网址 https://eslint.org/demo/[1],下载默认的配置文件。下载好后把文件修改成 .eslintrc.json 名称(名称前有一个点),然后把该文件剪切到项目根目录下。

需要注意的是,loader 的执行顺序是从右到左(对于一个规则,多个 loader 的情况,配置 .css laoder 时,use 项中有多个 loader)因此,less-loader 或者 sass-loader 先执行,让代码先转成原生的 CSS,然后使用 postcss-loader 优化 CSS 属性(比如添加属性后缀),然后是 css-loader 将 CSS 文件中 import 导入的文件添加进来,最后使用 style-loader 将 CSS 样式添加到 html 的 style 标签中;从下到上(对于一个多个规则,比如同是处理 .js 文件的配置,写了好几个规则(test)),因此,eslint-loader 应该放在所有 .js 规则中的最后一个(先检验,再做别的事情)。

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

也可以使用 options 中的 enforce 配置项:

代码语言:javascript
复制
{
    rules: [
        {
            test: /\.js$/,
            use: [
                loader: "eslint-loader",
                options: {
                    // 强制让这个 loader 最先执行
                    enforce: "pre"
                }
            ]
        },{
            test: /\.js$/,
            use: [
                loader: "babel-loader",
            ]
        }
    ]
}

enforce 默认值是 normal,除了 prenormal 之外,还有 post,表示强制最后执行在 normal 之后执行这个 loader。

html-withimg-loader

当我们在 HTML 模板中有 img 标签时,img 标签的 src 的路径并不会被 webpack 转化,因此需要使用 html-withimg-loader,使用之前同样需要先下载。然后配置:

代码语言:javascript
复制
{
    rules: [
        test: /\.html/,
        use: 'html-withimg-loader'
    ]
}

暴露全局变量

在 webapck 中使用 jquery 时,可以这么引入:

代码语言:javascript
复制
import $ from 'jquery';

但是这个 $ 变量并不在全局下(window)。如果我们想要将改变量暴露到全局中,需要使用 expose-loader

下载:yarn add expose-loader。将 jquery 模块暴露出来:

代码语言:javascript
复制
import $ from "expose-loader?$!jquery";

?$! 中的 $ 就是指被暴露的变量名(expose-loader ? ! 是固定格式)。

当然,如果不想这么写,也可以在 rules 中进行配置:

代码语言:javascript
复制
{
    rules: [
        test: require('jquery'),
        use: 'expose-loader?$'
    ]
}

配置好后,使用 jQuery 时,还需要进行引入:import $ from 'jquery'。如果不想每次都引入(或说不用引入),可以使用一个插件:provide-plugin。使用时不需要下载,webpack 自带,然后在 plugins 配置项中配置:

代码语言:javascript
复制
{
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery'
        })
    ]
}

如果你在 HTML 中引入了第三方模块使用 script 标签,但在开发中如果再使用 import $ from 'jquery',webpack 就会多打包一次。为了不让 webpack 这样做,可以添加一个配置:

代码语言:javascript
复制
module.exports = {
    plugins: [],
    // ...
    externals: {
        jquery: 'jQuery'
    }
}

使用 watch 简化操作

当代码一变化,就会自动打包。

代码语言:javascript
复制
// webpack.config.dev.js

module.exports = {
    watch: true,    // 开启监听
    watchOptions: {
        poll: 1000, // 每秒打包一次
        // 防抖,一直输入代码,停止输入 500 毫秒后再打包。
        aggreateTimeout: 500,
        // 不需要进行监控的文件或目录
        ignored: /node_modules/
    }
}

webpack 小插件

1. cleanWebpackPlugin

该插件需要下载,功能是每次新的打包完成后,旧的打包目录会自动被删除。该插件需要传入一个参数,你要删除的路径,要删除多个目录可以传入一个数组。

2. copyWebpackPlugin

该插件需要下载。功能是将没有指定为入口的目录中的文件拷贝到打包后的目录中。 格式:

代码语言:javascript
复制
new CopyWebpackPlugin([
    {from: '要拷贝的目录',to: '拷贝到哪里'}
]);

3. webpack.DefinePlugin

该插件是 webpack 自带的插件(不需要下载)。用它可以自定义环境变量。

代码语言:javascript
复制
{
    plugin: [
        new webpack.DefinePlugin({
            // DEV 变量就是一个环境变量
            DEV: JSON.stringify('dev'),
            PRODUCTION: JSON.stringify('production')
        }),
    ]
}

一般不使用这种方式配置环境变量。

4. BannerPlugin

该插件是 webpack 自带的,有一个字符串参数,表示版权说明。

代码语言:javascript
复制
{
    plugins: [
        new webpack.BannerPlugin("make 2019 by xxx"),
    ]
}

devServer 配置项

配置 devServer 之前需要先下载 webpack-dev-server: yarn add webpack-dev-server -D。 下载好之后,就可以在 webpack 配置项中去配置 webpack-dev-server 啦。

配置 devServer

devServer 的配置项很多,这里只对使用最多的做一下介绍。devserver 的配置应该是在开发环境下进行的。下面是一个简单的配置内容:

代码语言:javascript
复制
if(isDev){      // 如果是开发环境
    config.devServer = {
        // 设置 host(默认就是 localhost)
        // 如果你希望服务器外部可访问
        // 可以这样指定:127.0.0.1
        // 即:通过 IP 地址的形式
        host: "localhost",
        // 设置端口号
        port: "8888",
        // 告诉服务器从哪个目录中提供内容
        // 默认它会查找 index.html 文件作为页面根路径展示
        contentBase: path.join(__dirname,"../build"),
        // 这个publicPath代表静态资源的路径(打包后的静态资源路径)
        publicPath: '/build/',
        // 当设置成 true时,任意的 404 响应都可能需要被替代为 index.html
        historyApiFallback: true,
        // 是否开启 模块热替换功能
        hot: true,
        // 是否让浏览器自动打开(默认是 false)
        open: true,
        // 被作为索引文件的文件名。
        // 默认是 index.html,可以通过这个来做更改
        index: 'demo.html',

        // 使用代理服务器
        proxy: {
            '/api': {
                // 当请求 /api 的路径时,就是用 target 代理服务器
                target: "http://loaclhost:3000",
                // 重写路径
                pathRewrite: {
                    '/api': ''
                }
            }
        }
    }
}

有时候我们不想使用代理,只是想单纯的模拟数据。就可以使用 webpack 给我们提供的一个 before 函数:

代码语言:javascript
复制
{
    devServer: {
        // app 参数就是 express 框架的 express 实例
        before(app){
            app.get('/api',(req,res) => {
                // to do something...
            })
        }
    }
}

第三种方式,就是使用 webpack 的端口(服务端和 webpack(前端) 是一个端口)在服务端需要下载一个中间件:webpack-dev-middleware

代码语言:javascript
复制
yarn add webpack-dev-middleware -D

然后服务端写入以下代码:

代码语言:javascript
复制
const express = require("express");
const webpack = require("webpack");
const webpackMiddleware = require("webpack-dev-middleware");

// 引入写好的 webpack 配置文件
let config = require("./webpack.config.js");
let compiler = webpack(config);

// 绑定中间件
app.use(webpackMiddleware(compiler));

配置命令

来到 package.json 文件中,再添加一条命令,叫做 start,写下下面的内容:

代码语言:javascript
复制
{
    "script": {
        "build": "cross-env NODE_ENV='development' webpack --config config/webpack.config.dev.js",
        "start": "cross-env NODE_ENV=development webpack-dev-server --config config/webpack.config.dev.js"
    }
}

然后运行 npm start 就会自动打开浏览器,跳转到我们指定的 localhost:8888 端口。

有一点需要注意,在开发环境不要设置 publicPath,因为 开发环境下 devServer 执行打包的内容是在内存里的,如果设置了 publicPath 保存后页面反而不会有刷新。应在生产环境再用 publicPath。还有一点就是,每次修改配置项都要重新运行命令,这是很费时的一件事,如何在更新配置文件后不用再次重启服务呢?这在下面会说到。

historyApiFallback 更具体的配置

通过传入一个对象,比如使用 rewrites 这个选项,可进一步地控制。

代码语言:javascript
复制
{
    devServer: {
        historyApiFallback: {
            // 是个数组
            rewrites: [
                // b=表示 以 “/” 请求的页面,会返回 这个路径下的 html 文件
                { from: /^\/$/, to: '/views/landing.html' },
                { from: /^\/subpage/, to: '/views/subpage.html' },
                // 别的则会返回 404 页面
                { from: /./, to: '/views/404.html' }
            ]
        }
    }
}

devServer 中 publicPath 的配置

devServer 中的 publicPath 与 output 中的并不同。devServer 中的 publicPath 指的是 webpack-dev-server 的静态资源服务路径。假如我们打包的内容在 build 文件夹下,则 publicPath 应是 /build/,这里有个技巧,output 中指定的打包路径,比如:path: path.join(__dirname,'../build') 那么 devServer 的 publicPath 一般就是 join 方法中的那个 build。如果指定别的路径,很可能就会访问不到资源。

开启模块热替换功能

开启这个功能可以让我们修改文件并保持后,页面不会出现刷新的情况,页面中的内容是被动态更替了!这样减少了页面重新绘制的时间。在 devServer 中单纯的让 hot = true 是没有作用的,还需要一个 webpack 插件。这个插件是 webpack 内置的插件,不需要下载。具体配置步骤如下:

代码语言:javascript
复制
/**
 * 来到 webpack 配置文件
 * 引入 热更替插件
*/
const webpack = require('webpack');
// 来到 devServer 选项
{
    devServer: {
        hot: true
    },
    // 添加 plugin
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
    ]
}

配置好 webpack 之后,还需要在入口程序处检测 module.hot 是否存在(这个对象是在 webpack 打包后自动加入的)。 假如我们的程序入口文件是 index.js,可以这么来写:

代码语言:javascript
复制
// index.js
if(module.hot){
    // 调用 accept 方法开启热更替
    module.hot.accept();
}

上面步骤做完后,就可以使用 热更替了。如果有多个页面,则应为每个页面的入口作检验。

React 中使用热模块更替

在 React 中,index.js 常常做程序的入口,而 App.js 往往需要 index.js 的导入。module.hot.accept 方法可以接收两个参数,一个是路径字符串或者数组,另一个是回调函数。在 index.js 中可以这么来写:

代码语言:javascript
复制
import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

function render(){
    ReactDOM.render(
        <App />,
        document.getElementById('root')
    )
}

render();

if (module.hot) {
    console.log(module.hot);
    // 当第一个参数是数组时
    // 表示 有多个路径需要热模块更替
    // 回调用于在模块更新后触发的函数
    module.hot.accept('./App.jsx',() => {
        render();
    });
}

React 自己来提供了一个官方的热更替模块 —— react-hot-loader。使用它时需要下载: npm install react-hot-loader。使用时也需要配置。

  • 首先需要配置 webpack 文件:
代码语言:javascript
复制
// 更改 entry:
{
    entry: ['react-hot-loader/patch', '../src/index.js'],
}
  • 然后来到 .babelrc 文件,添加一个 plugin:
代码语言:javascript
复制
{
    "plugins": ["react-hot-loader/babel"]
}
  • 来到 index.js 文件处,你就可以直接把原来判断 module.hot 的内容给删掉了。而且 webpack 配置文件也不需要再引入 热更新插件(恢复没有热更新配置时的样子,但是 hot 项不要变成 false)。
  • 来到 App.js 文件,更改内容:
代码语言:javascript
复制
import { hot } from 'react-hot-loader';
function App(){
    // ....
}

// 最后这样导出:
export default hot(module)(App);

还没完,还应该重新下载一个包:yarn add @hot-loader/react-dom 这个包和 react-dom 一样,只是它有热替换功能。下载之后,在 webpack resolve 配置项中写入:

代码语言:javascript
复制
alias: {
    // 这样,你在引入 react-dom 时,就会引入这个包
    'react-dom': '@hot-loader/react-dom'
}

最后,重启服务,热更替模块就可以用了。使用 react-hot-loader 的好处就是,可以避免 React 组件的不必要渲染。

下一节将介绍 webpack 优化、代码分片与压缩,以及改造 create-react-app 的 webpack 配置,让其支持多页应用。

参考资料

[1]

eslint 配置文件下载: https://eslint.org/demo/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebTower 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 其他 loader 配置
    • eslint
      • html-withimg-loader
      • 暴露全局变量
      • 使用 watch 简化操作
      • webpack 小插件
        • 1. cleanWebpackPlugin
          • 2. copyWebpackPlugin
            • 3. webpack.DefinePlugin
              • 4. BannerPlugin
              • devServer 配置项
                • 配置 devServer
                  • 配置命令
                    • historyApiFallback 更具体的配置
                      • devServer 中 publicPath 的配置
                        • 开启模块热替换功能
                          • React 中使用热模块更替
                        • 参考资料
                        相关产品与服务
                        消息队列 TDMQ
                        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档