webpack简单配置及其用法一

本文章只是用于记录学习webpack的一些基础知识

webpack版本是4以上版本

一、初识webpack

1、webpack的配置文件名称

默认配置文件名称:webpack.config.js

修改默认配置文件名称:webpack  --config  指定名称

2、在项目中安装webpack,并且初始化

1)在项目的根目录下,运行npm init -y,生成package.json文件

2)在项目根目录下,安装webpack webpack-cli:npm i webpack webpack-cli -D  (webpack4以上将他们分开了)

通过运行下面脚本,如果输出了webpack webpack-cli的版本信息,就说明本地安装成功了

./node_modules/.bin/webpack -v

3、最基本的配置信息

项目的根目录信息如下:

1)在根目录下新建文件webpack.config.js,内容如下:

'use strict';

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    mode: 'production'
}

2)在根目录下新建文件夹src,在src下面新建文件helloworld.js   index.js

helloworld.js内容如下

export function helloworld() {
    return 'hello world!'
}

index.js内容如下:

import { helloworld } from './helloworld';

document.write(helloworld())

3、执行打包命令

默认情况下的打包命令:在项目根目录下执行下面的脚本

 ./node_modules/.bin/webpack

会在根目录下生成dist/bundle.js

在dist下新建文件index.html,内容如下:

<!doctype html lang="en">
  <meta charset="utf-8" />
  <title>test webpack</title>
  <body>
    <script src="./bundle.js" type="text/javascript"></script>
  </body>
</html>

修改默认的打包命令:

默认的打包命令:

./node_modules/.bin/webpack

在package.json文件添加属性script,添加下面脚本

script: {
  "build": "webpack"
}

执行npm run build就和执行.node_modules/.bin/webpack是一样的效果的

 二、webpack的基础用法

1、entry---指定webpack的打包入口

1)单入口---entry是一个字符串,对应源代码

module.exports={
 entry: './src/index.js'
}

2)多入口---entry是一个对象

module.exports={
  entry: {
    app: './src/app.js',
    adminApp: './src/adminApp.js'
  }
}

注意:不管单入口还是多入口,都只有一个output,单入口可以指定filename的具体名称,但是多入口需要用占位符[name].js(确保文件名称唯一)代替

2、output---告诉webpack如何将编译好后的文件输出到磁盘,对应转换后的代码

单入口的写法:

'use strict';

const path = require('path');

module.exports = {
    entry: './src/index.js',               // 入口字段的值为字符串
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'              // 可以指定输出的具体名称
    },
    mode: 'production'
}

多入口的写法:

'use strict';

const path = require('path');

module.exports = {
    entry: {                       // 对象
        index: './src/index.js',
        search: './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'     // 占位符,用entry里面定义的key值代替占位符
    },
    mode: 'production'
}

3、loader--webpack本身只支持js  json两种文件类型,通过loader去支持其他文件类型,并转化成有效模块,添加到依赖图中

loader本身是一个函数,入参为源文件,返回转换的结果,loader处理webpack不能处理的文件类型

 常见loader:

名称 描述
babel-loader 转换ES6、ES7等JS新特性语法
css-loader 支持css文件的加载和解析
less-loader 将less文件转换成css
ts-loader 将TS转换成JS
file-loader 进行图片、字体等的打包
raw-loader 将文件以字符串的形式导入
thread-loader 多进程打包js和css

loader的用法:在module属性里面的rules属性定义规则

'use strict';

const path = require('path');

module.exports = {
    entry: {
        index: './src/index.js',
        search: './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.txt$/,      // test--指定匹配规则
                use: 'raw-loader'    // use--指定使用的loader名称
            }
        ]
    },
    mode: 'production'
}

4、plugins---用于bundle文件的优化,资源管理,环境变量注入----作用于整个构建过程

loader不能完成的交由plugin处理

常见plugins

名称 描述
CommonsChunkPlugin 将chunks相同的模块代码提取成公共js
CleanWebpackPlugin 清理构建目录
ExtractTextWebpackPlugin 将css从bundle文件里提取成一个独立的css文件
CopyWebpackPlugin 将文件或者文件夹拷贝到构建的输出目录
HtmlWebpackPlugin

创建html文件去承载输出的bundle

UglifyjsWebpackPlugin 压缩js
ZipWebpackPlugin 将打包出的资源生成一个zip包

Plugins的用法:

'use strict';

const path = require('path');

module.exports = {
    entry: {
        index: './src/index.js',
        search: './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({template: '.src/index.html'})
    ],
    mode: 'production'
}

5、mode---用来指定当前的构建环境,production  development  none

设置mode,可以使用webpack的内置函数,默认值为production

mode的内置函数的功能

选项 描述
production

设置process.env.NODE_ENV = production,开启FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,

ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,

OccurrenceOrderPlugin,SideEffectsFlagPlugin,

TerserPlugin

development 设置process.env.NODE_ENV = development,开启NamedChunksPlugin  NamedModulesPlugin
none 不开启任何优化选项

三、webpack的使用

1、解析ES6语法------babel-loader,babel的配置文件.babelrc,如前面所写

资源解析:增加ES6的babel preset配置----解析ES6

{
  "presets":[
     "@babel/preset-env"
  ],
  "plugins": [
       "@babel/proposal-class-properties"
   ]
}

栗子:资源解析,解析ES6语法

1)安装依赖:@babel/core    @babel/preset-env (解析ES6语法)     babel-loader(将ES6语法转换成ES5)

npm i @babel/core  @babel/preset-env babel-loader -D

2)对babel的设置,在根目录下新建文件.babelrc,因为只需要解析ES6,所以只需要设置@babel/preset-env

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

3)设置loader的配置,在module.exports.module.ruls增加一条规则

    module: {
        rules: [
            {
                test: /\.js$/,
                use: 'babel-loader'
            }
        ]
    }

通过以上的配置,就可以用ES6语法进行开发了

栗子:资源解析:解析React JSX

1)在.babelrc添加React的babel preset配置

{
    "presets": [
        "@babel/preset-env",     // ES6的babel preset 配置
        "@babel/preset-react"   // React的babel preset配置
    ]
}

2)在项目中安装React相关插件

npm i react react-dom @babel/preset-react -D

3)在src目录下添加search.js

'use stric';

import React from 'react'
import ReactDOM from 'react-dom'

class Search extends React.Component {
    render() {
        return <div>Search text</div>
    }
}
ReactDOM.render(
    <Search />,
    document.getElementBtId('root')
)

4)在dist目录下新建main.html文件,引进打包出来的文件search.js

<!DOCTYPE html>
<body>
    <div id="root"></div>
    <script src="./search.js" type="text/script"></script>
</body>
</html

执行,npm run build就可以在浏览器打开文件main.html,看到效果

栗子:解析css,less,sass

资源解析:解析css

css-loader用于加载.css文件,并且转换成commonjs对象

style-loader将样式通过<style>标签插入到head中

module: {
  rules: [
    {
       test: /\.css$/,
       use: [
         'style-loader',
         'css-loader'
       ]
    }
  ]
}

首先,安装css-loader  style-loader

npm i style-loader css-loader -D

然后在配置中添加style-loader   css-loader,注意use里面的loader的执行顺序是后往前执行的,即先执行css-loader,在执行style-loader

   module: {
        rules: [
            {
                test: /\.js$/,
                use: 'babel-loader'
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            }
        ]
    }

通过以上的步骤,webpack就可以识别.css文件了

资源解析:解析less

在配置中添加less-loader,就可以识别.less文件内容了,less-loader将less转化成css

首先,安装less-loader,该loader依赖于less,所以两个都要安装

npm i less less-loader -D

 然后,在配置中添加.less文件的处理规则

    module: {
        rules: [
            {
                test: /\.js$/,
                use: 'babel-loader'
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            }
        ]
    }

通过以上步骤,webpack就可以识别.less文件了

栗子:资源解析:解析图片和字体

解析图片:file-loader用于处理文件

首先,在项目中安装file-loader

npm i file-loader -D

然后在module.rules添加如下配置

{
    test: /\.(png|jpg|jpeg|gif|svg)$/,
    use: 'file-loader'
}

这样就可以在项目中添加图片了,打包出来的dist根目录下面会有类似下面的文件名,这就是打包出来的图片

84869cf192b7950f4f6dd3f4c1173b51.jpg

解析字体:file-loader也可以用于处理字体

首先,在项目中安装file-loader

npm i file-loader -D

然后,在配置中module.rules添加如下配置

 {
    test: /\.(woff|woff2|eot|ttf|otf)$/,
    use: 'file-loader'
 }

接着,在入口的css文件或者其他css文件中引进字体库,如aa.otf

@font-face{
    font-family: 'aa';
    src: url(./aa.otf)
}
.search-text{
    font-size: 20px;
    color: red;
    font-family: 'aa';
}

通过以上步骤,webpack就可以识别引进的字体了

注意:file-loader和url-loader都可以对图片和字体等文件进行转换处理,但是url-loader可以设置小资源,并且可以自动base64的转换,其内部也是调用了file-loader

下面看下url-loader是如何设置小资源的时候自动转化base64从而减少http请求  limit属性的作用

首先,安装url-loader

npm i url-loader -D

然后,设置配置 

           {
                test: /\.(png|jpg|jpeg|gif|svg)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 10240
                        }
                    }
                ]
            }

上面配置的作用,就是将size<10240字节的图片进行base64的转码,但是js或者html的体积会增大

栗子:webpack中的文件监听

文件监听是在发现源码发生变化的时候,自动重新构建出新的输出文件

webpack开启监听模式,有两种方式:

1)启动webpack命令时,带上--watch参数

2)在配置webpack.config.js中设置watch:true

添加watch命令,这种方式的缺点就是每次需要手动刷新浏览器

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "watch": "webpack --watch"
  }

栗子:webpack中的热更新与及原理分析

1)热更新方式一:webpack-dev-server---不刷新浏览器;不输出文件而是放在内存中;结合HotModuleReplacementPlugin插件使用

热更新只是针对开发环境,所以mode:development

首先,在package.json文件的script添加执行语句

  "scripts": {
    "dev": "webpack-dev-server --open"
  }

其次,webpack-dev-server是在开发中使用,所以在webpack.config.js文件中将mode修改成development

mode: "development"

接着,配置plugins属性和devServer,由于HotModuleRepalcementPlugin属于webpack中的属性,需要引进webpack

const webpack = require('webpack')

    mode: 'production',
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    devServer: {
        contentBase: './dist', // 服务的基础目录
        hot: true // 开启热更新
    }

2)热更新方式二:使用webpack-dev-middleware

WDM将webpack输出的文件传输给服务器,适用于灵活的定制场景

热更新的原理分析:

Webpack Compile:将js编译成Bundle 

HMR Server:将热更新的文件输出给HMR Runtime

Bundle Server:提供文件在历览器的访问

HMR Runtime:会被注入到浏览器,更新文件的变化

bundle.js:构建输出的文件

栗子:文件指纹策略:chunkhash     contenthash     hash

文件指纹一般用于生产配置:mode:"production"

文件指纹:打包后输出的文件名的后缀,如下面所示_d45jf...

index_d45jfjde556nf.js

文件指纹的作用:

1)版本管理,只需要将修改过的文件发布,没修改过的文件的指纹不需要改变

2)没修改过的文件,还可以使用本地缓存,加速页面的访问速度

文件指纹的生成:

Hash:和整个项目的构建有关,只要项目文件有修改,整个项目构建的hash值就会修改

Chunkhash:和webpack打包的chunk有关,不同的entry会生成不同的Chunkhash值

Contenthash:根据文件内容来定义hash,文件内容不变的时候,则Contenthash不变

下面看下上述3个指纹的使用场景

1、打包出来的js文件使用chunkhash:设置output的filename,使用[chunkhash]

每一个入口对应的打包出来的chunk,就可以使用chunkhash

    entry: {
        index: './src/index.js',
        search: './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name][chunkhash:8].js'
    }

2、打包出来的css使用contenthash:设置MiniCssExtractPlugin的filename,使用contenthash]

一般在项目中使用css-loader,style-loader会将css插入html中的heade中,因此需要plugin:MiniCssExtractPlugin将css提取出来

首先,安装插件:

npm i mini-css-extract-plugin -D

然后,添加配置:

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
    plugins: [
        new MiniCssExtractPlugin({
            filename:`[name][contenthash:8].css`
        })
    ]

注意:由于style-loader是通过style标签将css插入heade中,这和mini-css-extract-loader的功能相反--将css提取到单独的文件里面 ,所以style-loader和mini-css-extract-loader的loader不能共存

    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            }
        ]
    },

3、设置file-loader、url-loader的name,使用[hash]

            {
                test: /\.(png|jpg|jpeg|gif|svg)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'img/[name][hash:8].[ext]'
                        }
                    }
                ]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'font/[name][hash:8].[ext]'
                        }
                    }
                ]
            }
占位符名称 含义
[path] 文件的相对路径
[folder] 文件所在的文件夹
[name] 文件名
[contenthash] 文件内容的hash,默认是md5生成
[hash] 文件内容的hash,默认是md5生成
[ext] 资源后缀名
[emoji] 一个随机的指代文件内容的emoji

在package.json文件里面可以写两个执行命令,以区分生产和开发环境使用那个webpack配置

  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --config webpack.dev.js --open"
  }

栗子:代码压缩,包括html   css   js压缩

js文件的压缩:在webpack4以上是内置了uglifyjs-webpack-plugin,默认打包出来的文件是压缩过了的

css文件的压缩:使用optimize-css-assets-webpack-plugin,同时使用cssnano

html文件压缩:修改html-webpack-plugin,设置压缩参数

css文件压缩

首先,安装插件optimize-css-assets-webpack-plugin,cssnano

npm i cssnano -D
npm i optimize-css-assets-webpack-plugin -D

然后,在webpack.prod.js文件中配置

const OptimizeCssAssetsWebpackPlugin= require('optimize-css-assets-webpack-plugin')
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name_[contenthash:8].css'
        }),
        new OptimizeCssAssetsWebpackPlugin({
            assetNameRegExp: /\.css$/g,
            cssProcessor: require('cssnano')
        })
    ]

通过以上步骤就可以将css中的空白符号和注释等去掉

html文件压缩

首先,安装html-webpack-plugin插件

npm i html-webpack-plugin -D

然后,在配置中添加如下配置

const HtmlWebpackPlugin = require('html-webpack-plugin')
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name_[contenthash:8].css'
        }),
        new OptimizeCssAssetsWebpackPlugin({
            assetNameRegExp: /\.css$/g,
            cssProcessor: require('cssnano')
        }),
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/search.html'), // html模板所在的一个位置
            filename: 'search.html', // 指定html打包出来的以恶搞文件名称
            chunks: ['search'], // 生成的html要使用那些chunks
            inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
            minify: {
                html5: true,
                collapseWhitespace: true,
                preserveLineBreaks: true,
                minifyCSS: true,
                minifyJS: true,
                removeComments: true
            }
        }),
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/index.html'), // html模板所在的一个位置
            filename: 'index.html', // 指定html打包出来的以恶搞文件名称
            chunks: ['index'], // 生成的html要使用那些chunks
            inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
            minify: {
                html5: true,
                collapseWhitespace: true,
                preserveLineBreaks: true,
                minifyCSS: true,
                minifyJS: true,
                removeComments: true
            }
        })
    ]

这里由于是多页面应用,所以html-webpack-plugin使用了两次来配置相应的信息

三、webpack的进阶用法

栗子:自动清理构建目录产物

问题:每次构建的时候不会清理目录,造成构建的输出目录output文件越来越多

解决方法1:通过npm scripts 清理构建目录

rm -rf ./dist && webpack

解决方法2:使用clean-webpack-plugin,默认在构建前会自动删除output指定的输出目录

避免每次构建前都需要手动删除dist目录

首先:安装插件clean-webpack-plugin

 npm i clean-webpack-plugin -D

然后,在webpack.dev.js   webpack.prod.js文件里面引进插件

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    plugins: [
        new CleanWebpackPlugin()
    ]

通过以上步骤,在每次打包的时候,构建出来的目录里面的文件数量是不会增加的

栗子:对css的一些增强功能使用

postCss插件,autoprefixer自动补齐css3前缀

首先,安装插件

npm i postcss-loader autoprefixer -D

然后在配置中添加如下规则 

    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: () => {
                                    require('autoprefixer')({
                                        browsers: ['last 2 version', '>1%', 'ios 7']
                                    })
                                }
                            }
                        }
                    }
                ]
            }
        ]
    },

栗子:移动端css  px自动转换成rem---需要适应不同的分辨率

传统方式:css媒体查询实现响应式布局

缺陷:需要写多套适配样式代码

@media screen and (max-width: 980px) {
    .header{
        width: 900px;
    }
}
@media screen and (max-width: 480px) {
    .header{
        width: 400px;
    }
}

使用px2rem-loader,,,待续

栗子:静态资源内联

资源内联的意义:

1)代码层面:

    页面框架的初始化脚本

    上报相关打点

     css内联避免页面闪动

  2)请求层面:减少HTTP请求次数

       小图片或者字体内联(url-loader)

html和js内联

raw-loader内联html

raw-loader内联js

raw-loader的基本原理:读取文件内容,返回string,然后插入到对应的位置

使用的是html-webpack-plugin,默认的模板引擎是ejs引擎,所以可以识别语法:${代码}

<!DOCTYPE html>
<html lang="en">
    <head>
        <!-- 引进meta信息 -->
        <!-- 使用raw-loader,引进的文件的路径 -->
        ${ require('raw-loader!./meta.html') }
        <title>kuai</title>
        <!-- 引进脚本 -->
        <!-- 使用raw-loader,在用babel-loader解析,引进脚本的路径 -->
        ${require('raw-loader!babel-loader!../node_modules/leven/index.js')}
    </head>
    <body></body>
 </html>

c s s内联

1)借助style-loader

    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: 'style-loader',
                        options: {
                            insertAt: 'top',     // 样式插入到<head>中
                            singleton: true     // 将所有到style标签合并成一个
                        }
                    },
                    'css-loader',
                    'sass-loader'
                ]
            }
         }
    }

2)html-inline-css-webpack-plugin

栗子:多页面应用打包通用方案

多页面应用概念:每一次页面跳转的时候,后台服务器都会返回一个新的html文档,这种类型的网站也就是多页网站,也叫多页应用

优点:1)页面解耦    2)seo更加友好

A、多页面打包基本思路:每个页面对应一个entry,一个html-webpack-plugin

缺点:每次新增或者删除页面的时候,都需要修改webpack配置

B、动态获取entry,和设置html-webpack-plugin的数量,利用glob.sync

规则:这种方法需要约定,每个页面的入口文件命名都为index.js,html模版文件名为index.html

下面的语句的路径,是获取根目录下的src目录下的一级目录下的index.js,即:src/search/index.js

entry: glob.sync(path.join()__dirname, './src/*/index.js')

glob.sync同步读取文件,返沪数组,数组内容为匹配上的路径名

1)安装glob插件

npm i glob -D

2)修改文件路径如下图所示,每个文件夹下都有index.js,index.html

3)修改配置

先是利用glob.sync获取到入口文件的路径

然后对存放路径的数组遍历,获取每一个路径的的文件名,为每个入口添加插件

最后,将入口和插件配置到odule.exports里面去

const glob = require('glob')

const setMPA = () => {
    const entry = {}
    const htmlWebpackPlugins = []
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'))  // 读取文件路径
    console.log(entryFiles)

    entryFiles.map(item => {
        const match = item.match(/src\/(.*)\/index\.js$/) // 匹配为数组,第一个是匹配上的路径,第二个是()里面的内容
        const pageName = match && match[1]
        entry[pageName] = item    // 文件名
        htmlWebpackPlugins.push(  // 每个入口添加插件
            new HtmlWebpackPlugin({
                template: path.join(__dirname, `src/${pageName}/index.html`), // html模板所在的一个位置
                filename: `${pageName}.html`, // 指定html打包出来的文件名称
                chunks: [pageName], // 生成的html要使用那些chunks
                inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
                minify: {
                    html5: true,
                    collapseWhitespace: true,
                    preserveLineBreaks: true,
                    minifyCSS: true,
                    minifyJS: true,
                    removeComments: true
                }
            })
        )
    })

    return {
        entry,
        htmlWebpackPlugins
    }
}
const {entry, htmlWebpackPlugins} = setMPA()
module.exports = {
    entry,
    plugins: [
        new CleanWebpackPlugin(),
        // new HtmlWebpackPlugin({
        //     template: path.join(__dirname, 'src/search.html'), // html模板所在的一个位置
        //     filename: 'search.html', // 指定html打包出来的以恶搞文件名称
        //     chunks: ['search'], // 生成的html要使用那些chunks
        //     inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
        //     minify: {
        //         html5: true,
        //         collapseWhitespace: true,
        //         preserveLineBreaks: true,
        //         minifyCSS: true,
        //         minifyJS: true,
        //         removeComments: true
        //     }
        // }),
        // new HtmlWebpackPlugin({
        //     template: path.join(__dirname, 'src/index.html'), // html模板所在的一个位置
        //     filename: 'index.html', // 指定html打包出来的以恶搞文件名称
        //     chunks: ['index'], // 生成的html要使用那些chunks
        //     inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
        //     minify: {
        //         html5: true,
        //         collapseWhitespace: true,
        //         preserveLineBreaks: true,
        //         minifyCSS: true,
        //         minifyJS: true,
        //         removeComments: true
        //     }
        // })
    ].concat(htmlWebpackPlugins)
}


栗子:使用SourceMap

定义:SourceMap就是一个存储源代码与编译代码对应位置映射的信息文件

作用:进行debugger和错误排查等调试用的,一般开发环境开启,线上环境关闭

对应的webpack配置项为devtool,值设置可以参考文档https://v4.webpack.js.org/configuration/devtool/#devtool

SourceMap的关键字:匹配规则:"^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$"

关键字 作用
eval 编译后的代码,使用eval包裹
source map 编译后的代码,产生.map文件
cheap 编译后的代码,不包含列信息
inline 编译后的代码,将.map作为DataURI嵌入,不单独生成.map文件
module 编译后的代码,包含loader的source map
module.exports = {
 devtool: 'eval'
}

栗子:提取公共资源

基础库分离

思路:将react  react-dom基础包通过cdn引入,不打入bundle中

方法1: 使用html-webpack-externals-plugin插件

首先,安装插件

html-webpack-externals-plugin

然后,修改配置文件

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')


module.exports = {
  plugins: [
       new HtmlWebpackExternalsPlugin({
            externals: [
                {
                    module: 'react',
                    entry: 'https://11.url.cn/now/lib/16.2.0/react.min.js',   // 本地文件,或者cdn文件
                    global: 'React'
                },
                {
                    module: 'react-dom',
                    entry: 'https://11.url.cn/now/lib/16.2.0/react-dom.min.js',
                    global: 'ReactDOM'
                }
            ]
        })
  ]
}

最后,在模版文件,引进cdn脚本,这样就可以了

<!DOCTYPE html>
<head>
    <title>document</title>
</head>
<body>
    <div id="root"></div>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react.min.js"></script>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react-dom.min.js"></script>
</body>
</html>

方法2:使用webpack4内置的SplitChunksPlugin进行公共脚本分离,替代CommonsChunkPlugin插件,test:匹配出需要分离的包

满足设定的条件,才会打出公共包

chunks参数说明:

async:异步引入的库进行分离(默认)

inital:同步引入的库进行分离

all:所有引入的库进行分离(推荐)

minChunks:设置最小引用次数

minSize:分离包体积大小

将插件react  react-dom单独打包出来

// 在htmlWebpackPlugin里面要添加打包出来的包名,才能使用,chunks: ['vendors', pageName]
const setMPA = () => {
    const entry = {}
    const htmlWebpackPlugins = []
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'))
    console.log(entryFiles)

    entryFiles.map(item => {
        const match = item.match(/src\/(.*)\/index\.js$/)
        const pageName = match && match[1]
        entry[pageName] = item
        htmlWebpackPlugins.push(
            new HtmlWebpackPlugin({
                template: path.join(__dirname, `src/${pageName}/index.html`), // html模板所在的一个位置
                filename: `${pageName}.html`, // 指定html打包出来的文件名称
                chunks: ['vendors', pageName], // 生成的html要使用那些chunks,这里添加公共的包名
                inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
                minify: {
                    html5: true,
                    collapseWhitespace: true,
                    preserveLineBreaks: true,
                    minifyCSS: true,
                    minifyJS: true,
                    removeComments: true
                }
            })
        )
    })

    return {
        entry,
        htmlWebpackPlugins
    }
}



module.exports = {
 optimization: {
        splitChunks: {
            cacheGroups: {
                commons: {
                    test: /(react|react-dom)/
                    name: 'vendor',
                    chunks: 'all'
                }
            }
        }
    }
}

生成一个通用的公共代码抽离的配置

module.exports = {
     optimization: {
        splitChunks: {
            minSize: 0, // 有引用就打出包
            cacheGroups: {
                commons: {
                    name: 'common', // 打包出来的文件名
                    chunks: 'all',
                    minChunks: 2 // 公共文件引用次数至少2次
                }
            }
        }
    }
}

栗子:tree shaking

概念:一个模块可能有多个方法,只要其中一个方法被使用,则整个文件就会被打包到bundle文件里面。tree shaking就是把用到的方法才打包到bundle里面,没用到的方法会在uglify阶段擦除掉

使用:webpack默认支持,在.babelrc里设置modules: false;production mode默认开启

要求:必须是ES6语法,CJS方式不支持

DEC:

1) 代码永远不会被执行,不可到达,如下面语句永远不会执行

if (false) {
     return 1
}

2)代码执行的结果不会被用到,比如,写了一个方法,并且返回结果,但该结果没有使用

3)代码只会影响死变量(只写不读),比如定义了一个变量,条件变了会修改变量的值,但是并没有用到该变量

栗子:scope hoisting的使用

现象:构建后的代码存在存在大量的必包代码

问题:大量函数闭包包裹代码,导致体积增大(模块越多越明显);运行带代码时,创建的函数作用域变多,内存开销变大

模块转换分析:模块转化成模块初始化函数

1)被webpack转换后的模块会带上一层包裹

2)import会被转换成_webpack_require

webpack的模块机制

1)打包出来是一个IIFE(匿名闭包)

2)modules是一个数组,每一项是一个模块初始化函数

3)_webpack_require用来加载模块,返回module.exports

4)通过WEBPACK_REQUIRE_METHOD(0)启动程序

scope hoisting原理

原理:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突;当模块被引用了一次,就会被内联进来,不会创建新的的函数声明,否则还是会的

对比:通过scope hoisting可以减少函数声明代码和内存开销

scope hoisting使用

webpack mode为production默认开启,不需要任何配置

必须是ES6语法,CJS不支持

如果mode不是production,则需要配置:module.exports.plugins = [new webpack.optimize.ModuleConcatenationPlugin()]

栗子:代码分割和动态import

代码分割的意义:

对应大的web应用而言,将所有的代码都放在一个文件中显然不够有效,特别是当某部分代码是在某些时候才才会被用到。webpack又一个功能,就是将代码分割成chunks(语块),当代码运行到需要他们的时候在加载

适用场景:

1)抽离相同代码到一个共享块

2)脚本懒加载,使用初始下载的代码更小

懒加载JS脚本的方式:

1)CommonJS:require.ensure()

2)ES6:动态import(目前还没有原生支持,需要babel转换)

如何使用动态import

1)安装babel插件:npm i @babel/plugin-syntax-dynamic-import -D

2)ES6:动态import,在.babelrc文件里面添加:"plugins":["@babel/plugin-syntax-dynamic-import"]

3)在test.js文件添加代码

import React from 'react'
export default () => <div>danymic import</div>

4)在index.js,动态添加该组件:点击图片的时候,在加载代码,可以在控制台看到效果,一个是点击图片的时候,在network会有一个请求,或者在html会插入<script>

'use stric';

import React from 'react'
import ReactDOM from 'react-dom'
import './search.less'
import sea from './images/sea.jpg'
import  '../../common'

class Search extends React.Component {
    constructor () {
        super(...arguments)
        this.state = {
            Text: null  // 定义组件变量
        }
    }
    loadComponent () {
        import('./test.js').then(text => { // 动态引进组件
            this.setState({
                Text: text.default
            })
        })
    }
    render() {
        return <div className="search-text">
            {
                Text ? <Text /> : null
            }
            Search text<img src={ sea } onClick={ this.loadComponent.bind(this) }/>
        </div>
    }
}
ReactDOM.render(
    <Search />,
    document.getElementById('root')
)

栗子:在webpack中使用ESLint

使用ESLint的必要性:js报错,可能导致页面白屏,避免在发布代码前发现问题

行业内优秀的eslint规范实践:

1)Airbnb:eslint-config-airbnb适合react项目的语法检查

2)eslint-config-airbnb-base适合vue项目

制定团队的ESlint规范

1)不重复造轮子,基于eslint:recommend配置并改进

2)能够帮助发现代码错误的规则,全部开启

3)帮助保持团队代码风格统一,而不是限制开发体验

ESlint如何执行落地

1)和CI/CD系统集成

2)和webpack集成

方案一:webpack与CI/CD集成

本地开发阶段增加precommit钩子

首先,安装husky

npm i husky -D

然后,在package.json添加npm script,通过lint-staged增量检查修改的文件

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "rm -rf ./dist/ && webpack --config webpack.prod.js",
    "watch": "webpack --watch",
    "dev": "rm -rf ./dist/ && webpack --config webpack.dev.js",
    "clear": "rm -rf ./dist/",
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "linters": {
      "*.{js,csss}": ["eslint --fix", "git add"]
    }
  }

方案二:webpack与eslint集成

使用eslint-loader,构建时检查js规范,eslint官方文档:http://eslint.cn/docs/rules/indent

以eslint-config-airbnb为栗子

首先,安装插件,参考文档:https://www.npmjs.com/package/eslint-config-airbnb

npm i eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y -D

然后,在配置文件中添加如下eslint-loader规则

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    'babel-loader',
                    'eslint-loader'
                ]
            }
   }

接着,在项目根目录下创建文件.eslintrc.js,这里用的是js所以以.js文件结尾,同时需要用module.exports

module.exports = {
    "parser": "babel-eslint", // 指定解析器
    "extends": "airbnb", // 继承airbnb
    "env": {
        "browser": true, // 指定环境
        "node": true
    },
    "rules": {
        "indent": ["error", 4] // 修改规则
    }
}

由于解析器使用babel-eslint,需要继承airbnb,所以需要安装他们

npm i eslint-loader eslint-config-airbnb -D

接着,构建的时候,不符合规定的语法都会报错

栗子:webpack打包组件和库

作用:webpack可以打包应用,也可以打包js库

目的:实现一个大整数加法库的打包

1)需要打包压缩版和非压缩版

2)支持AMD/CJS/ESM模块引入,script引进

// 支持ESM
import * as bigNumber from 'big-number'
bigNumber.add('888', '8')

// 支持CJS
const bigNumbers = require('big-number)
bigNumber.add('888', '8')

// 支持AMD
require(['big-number'], function (big-number) {
    bigNumber.add('888', '8')
})



// 支持脚本的引进方式
<script type="text/javascript" src="https://unpkg.com/big-number"></script>
<script>
    // 全局变量
    bigNumber.add('8888', '8')
    // window对象的一个属性
    window.bigNumber.add('8888', '8')
</script>

如何将库暴露出去

module.exports = {
    mode: 'production',
    entry: {
        "big-number": "./src/index.js",
        "big-number.min": "./src/index.js"
    },
    output: {
        filename: "[name].js",
        library: "bigNumber.js", // 指定库的全局变量
        libraryExport: "default",
        libraryTarget: "umd" // 支持库引入的方式
    }
}

首先,新建项目big-number,对项目进行webpack初始化

mkdir big-number
npm init -y
npm i webpack webpack-cli -D

然后,在项目根目录下创建src/index.js,内容如下

export default function add (a, b) {
    // a,b为string类型
    let i = a.length - 1
    let j = b.length - 1

    let carry = 0 // 进位符
    let ret = '' // 返回的结果
    // 从个位开始加起,一直向十位,百位
    while (i >= 0 || j >= 0) {
        let x = 0
        let y = 0
        let sum
        if (i >= 0) {
            x = a[i--] - '0' // 将字符串转为数字
        }
        if (j >= 0) {
            y = b[j--] - '0' // 将字符串转为数字
        }
        sum = x + y + carry
        if (sum >= 10) {
            carry = 1
            sum -= 10
        } else {
            carry = 0
        }
        ret = sum + ret  // 相当于字符串拼接
    }
    if (carry) {
        ret += carry
    }
    return ret
}

其次,配置文件,只对指定文件进行代码压缩。使用插件terser-webpack-plugin,只对指定文件进行代码压缩。mode="production"默认压缩文件,这里不用uglifyplugin是因为uglifyplugin插件遇到es6语法会报错,而TerserPlugin不会

npm i terser-webpack-plugin -D

接着,编写配置文件webpack.config.js

const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
    mode: 'none', // 将压缩都去掉
    entry: {
        'big-number': './src/index.js',
        'big-number.min': './src/index.js'
    },
    output: {
        filename: '[name].js',
        library: 'bigNumber',
        libraryTarget: 'umd',
        libraryExport: 'default'
    },
    optimization: { // 设置该选项,设置.min.js文件才压缩
        minimize: true,
        minimizer: [
            new TerserPlugin({
                include: /\.min\.js$/
            })
        ]
    }
}

在接着,在package.json文件添加脚本

"build": "rm -rf ./dist/ && webpack"

在接着,设置入口文件,package.json的main字段为index.js--这个就是入口文件,根据环境引不同的包,在根目录下创建index.js文件


if (process.env.NODE_ENV === 'production') { // mode = 'production'则process.env.NODE_ENV = 'production'
    module.exports = require('./dist/big-number.min.js')
} else {
    module.exports = require('./dist/big-number.js')
}

通过npm login 登陆你的账号,就可以通过npm publish发布你的包了

栗子:webpack实现SSR打包

SSR代码实现思路

服务端:1)使用react-dom/server的renderToString方法将react组件渲染成字符串;---因为服务端没有window  document等对象

              2)服务端路由返回对应的模板

客户端:1)打包出针对服务端的组件

首先,在项目根目录下新建针对ssr的配置文件,webpack.ssr.js

然后,在package.json文件添加脚本

    "build:ssr": "rm -rf ./dist/ && webpack --config webpack.ssr.js"

接着,创建服务端的目录,在根目录下创建server/index.js这个就是服务端的脚本,服务端采用express,所以先安装express

npm i express -D

再接着,编写server文件,再server/index.js添加如下文件

const express = require('express')
const { renderToString } = require('react-dom/server') // 将客户端的组件渲染成字符串
const SSR = require('../dist/search-ssr') // 将组件引进来

// 设置监听的端口
const server = (port) => {
    const app = express() // 实例化express
    app.use(express.static('dist')) // 用静态目录
    // 写一个路由
    app.get('/search', (req, res) => {
        const html = renderMarku(renderToString(SSR))
        // res.status(200).send(renderToString(SSR)) // 这样返回的是一个字符串,但时机需要返回html页面,所以需要模板包装
        res.status(200).send(html)
    })
    // 监听端口
    app.listen(port, () => {
        console.log(`server is running on port: ${port}`)
    })
}
server(process.env.PORT || 3000)

const renderMarku = (str) => {
    return `
         <!doctype html>
            <head>
                <title>document</title>
            </head>
            <body>
                <div id=""root>${str}</div>
            </body>
        </html>
    `
}

在接着编写客户端代码,在src/search/index-server.js添加如下代码

'use strict';

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const glob = require('glob')
// const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')

const setMPA = () => {
    const entry = {}
    const htmlWebpackPlugins = []
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index-server.js')) // 修改入口文件
    console.log(entryFiles)

    entryFiles.map(item => {
        const match = item.match(/src\/(.*)\/index-server\.js$/)
        const pageName = match && match[1]
        if (pageName) {
            // 有的时候才添加
            entry[pageName] = item
            htmlWebpackPlugins.push(
                new HtmlWebpackPlugin({
                    template: path.join(__dirname, `src/${pageName}/index.html`), // html模板所在的一个位置
                    filename: `${pageName}.html`, // 指定html打包出来的文件名称
                    chunks: ['vendors', pageName], // 生成的html要使用那些chunks
                    inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
                    minify: {
                        html5: true,
                        collapseWhitespace: true,
                        preserveLineBreaks: true,
                        minifyCSS: true,
                        minifyJS: true,
                        removeComments: true
                    }
                })
            )
            }
    })

    return {
        entry,
        htmlWebpackPlugins
    }
}
const {entry, htmlWebpackPlugins} = setMPA()

module.exports = {
    entry,
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]-server.js', // 删除的hash值去掉
        libraryTarget: 'umd',
        publicPath: './'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    'babel-loader'
                ]
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: () => {
                                    require('autoprefixer')({
                                        browsers: ['last 2 version', '>1%', 'ios 7']
                                    })
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(png|jpg|jpeg|gif|svg)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'img_[name][hash:8].[ext]'
                        }
                    }
                ]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'font/[name][hash:8].[ext]'
                        }
                    }
                ]
            }
        ]
    },
    mode: 'production',
    plugins: [
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename: '[name_[contenthash:8].css'
        }),
        new OptimizeCssAssetsWebpackPlugin({
            assetNameRegExp: /\.css$/g,
            cssProcessor: require('cssnano')
        })
    ].concat(htmlWebpackPlugins),
    optimization: {
        splitChunks: {
            minSize: 0, // 有引用就打出包
            cacheGroups: {
                commons: {
                    // test: /(react|react-dom)/, // /(react|react-dom)/
                    name: 'common',
                    chunks: 'all',
                    minChunks: 2 // 公共文件引用次数至少2次
                }
            }
        }
    }
}

在接着,修改webpack.ssr.js文件,主要是output去掉hash,加上-ssr;设置libraryTarget:‘umd’;修改入口文件名

'use strict';

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const glob = require('glob')
// const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')

const setMPA = () => {
    const entry = {}
    const htmlWebpackPlugins = []
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index-server.js')) // 修改入口文件
    console.log(entryFiles)

    entryFiles.map(item => {
        const match = item.match(/src\/(.*)\/index-server\.js$/)
        const pageName = match && match[1]
        if (pageName) {
            // 有的时候才添加
            entry[pageName] = item
            htmlWebpackPlugins.push(
                new HtmlWebpackPlugin({
                    template: path.join(__dirname, `src/${pageName}/index.html`), // html模板所在的一个位置
                    filename: `${pageName}.html`, // 指定html打包出来的文件名称
                    chunks: ['vendors', pageName], // 生成的html要使用那些chunks
                    inject: true, // 为true,则打包出来的chunk会自动注入到html文件中
                    minify: {
                        html5: true,
                        collapseWhitespace: true,
                        preserveLineBreaks: true,
                        minifyCSS: true,
                        minifyJS: true,
                        removeComments: true
                    }
                })
            )
            }
    })

    return {
        entry,
        htmlWebpackPlugins
    }
}
const {entry, htmlWebpackPlugins} = setMPA()

module.exports = {
    entry,
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]-server.js', // 删除的hash值去掉
        libraryTarget: 'umd'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    'babel-loader'
                ]
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: () => {
                                    require('autoprefixer')({
                                        browsers: ['last 2 version', '>1%', 'ios 7']
                                    })
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(png|jpg|jpeg|gif|svg)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'img_[name][hash:8].[ext]'
                        }
                    }
                ]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'font/[name][hash:8].[ext]'
                        }
                    }
                ]
            }
        ]
    },
    mode: 'production',
    plugins: [
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename: '[name_[contenthash:8].css'
        }),
        new OptimizeCssAssetsWebpackPlugin({
            assetNameRegExp: /\.css$/g,
            cssProcessor: require('cssnano')
        })
    ].concat(htmlWebpackPlugins),
    optimization: {
        splitChunks: {
            minSize: 0, // 有引用就打出包
            cacheGroups: {
                commons: {
                    // test: /(react|react-dom)/, // /(react|react-dom)/
                    name: 'common',
                    chunks: 'all',
                    minChunks: 2 // 公共文件引用次数至少2次
                }
            }
        }
    }
}

执行npm run build:ssr就可以构建成功了

再接着,运行node端服务

node server/index.js

但是会抛出错误,node服务端没有这些对象

1)ReferenceError: self is not defined
2)ReferenceError: window is not defined

处理上述问题,需要在server/index.js增加hack

if (typeof windwo === 'undefined') {
    global.windows = {}
}
if (typeof self === 'undefined') {
    global.self = {}
}

在运行node端服务的时候,报下面错误

webpack打包时Error: Automatic publicPath is not supported in this browser

原因:因为打包资源后不能解析“./”之类的路径,需要通过publicPath配置

在webpack.ssr.js文件的output.publicPath = './'    不一定设置成该值,根据公式自行配置

在运行服务端脚本,就可以了

webpack ssr打包问题

1)浏览器的全局变量(Node.js没有window  docum)

A、组件适配,将不兼容的组件根据打包环境进行适配

B、请求适配,将fetch  ajax发送请求的写法改成isomorphic-fetch axios

2)样式问题(Node.js无法解析css)

方案一:服务端打包通过ignore-loader忽略掉css的解析

方案二:将style-loader替换成isomorphic-style-loader

解决样式不显示问题

使用打包出来的浏览器端·html为模版

设置占位符,动态插入组件

首先,修改server/index.js中的模版

if (typeof windwo === 'undefined') {
    global.windows = {}
}
if (typeof self === 'undefined') {
    global.self = {}
}

// 将打包出来的模版引进来
const express = require('express')
const { renderToString } = require('react-dom/server') // 将客户端的组件渲染成字符串
const SSR = require('../dist/search-server') // 将组件引进来

const path = require('path')
const fs = require('fs')
const template = fs.readFileSync(path.join(__dirname, '../dist/search.html'), 'utf-8')
// 设置监听的端口
const server = (port) => {
    const app = express() // 实例化express
    app.use(express.static('dist')) // 用静态目录
    // 写一个路由
    app.get('/search', (req, res) => {
        const html = renderMarku(renderToString(SSR))
        // res.status(200).send(renderToString(SSR)) // 这样返回的是一个字符串,但时机需要返回html页面,所以需要模板包装
        res.status(200).send(html)
    })
    // 监听端口
    app.listen(port, () => {
        console.log(`server is running on port: ${port}`)
    })
}
server(process.env.PORT || 3000)

// 占位符
const renderMarku = (str) => {
    // 这里的字符串的值,是在html模版中的占位符一致
    return template.replace('<!--HTML_PLACEHOLDER-->', str)
}

在search.html文件添加占位符

<!DOCTYPE html>
<head>
    <title>document</title>
</head>
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react.min.js"></script>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react-dom.min.js"></script>
</body>
</html>

首屏数据如何处理

服务端获取数据

替换占位符

首先,在search目录下新增mock数据的文件data.json

{
	"sodar_query_id": "PmvfX_X5Eomk8AXttY24Dg",
	"injector_basename": "sodar2",
	"bg_hash_basename": "PbZvCEkorD5rxjWOexle1_regFmuc5-vrUA2zacPm4s",
}

然后,在search.html添加占位符

<!DOCTYPE html>
<head>
    <title>document</title>
</head>
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react.min.js"></script>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react-dom.min.js"></script>
    <!--INITAL_DATA_PLACEHOLDER-->
</body>
</html>

在server.index.js引进数据,这里只是把主要的写出来,其他的同上

// 引进数据
const data = require('../src/search/data.json')
// 占位符
const renderMarku = (str) => {
    const dataStr = JSON.stringify(data)
    // 这里的字符串的值,是在html模版中的占位符一致
    return template.replace('<!--HTML_PLACEHOLDER-->', str)
    .replace('<!--INITAL_DATA_PLACEHOLDER-->', `<script>windwo.__inital_data=${dataStr}</script>`)
}

栗子:优化构建时命令行的显示日志

统计信息stats

preset alternative description
“errors-only” none 只在错误时输出
“minimal” none 只在错误时或有新的编译时输出
”none“ false 没有输出
“normal” true 标准输出
”verbose“ none 全部输出

在配置中添加:

module.exports.stats = 'errors-only'

使用插件friendly-errors-webpack-plugin,构建success warning  error日志提示

npm i friendly-errors-webpack-plugin -D
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
module.exports.plugins = [new FriendlyErrorsWebpackPlugin()]

猜你喜欢

转载自blog.csdn.net/tangxiujiang/article/details/110728504