命中注定拯救前端的,应该是 webpack (二)

250 阅读5分钟

接上篇,命中注定拯救前端的,应该是 webpack (一) - 掘金 (juejin.cn)

我们接着来完善开发环境。

由于 npm run start 有歧义,我们将它更改为 npm run build.

CSS 文件单独引入

上一篇我们已经完成了 less 构建,但打包后,我们在 dist/index.html 并没有看到 css 的引入。由于我们是在 js 文件中引入的 less ,所以 webpack 将它们打包到了一起。

这样做危险的一点在于,如果我们有时候需要设定 script 的 defer 属性,那么 最开始将没有页面样式。

因此,我们将 css 单独打包。

 npm install mini-css-extract-plugin -D

在 webpack.config.js 中引入:

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
...
 plugins:[ // 配置插件
        new HtmlWebpackPlugin({
            template: './index.html',
            scriptLoading: 'blocking',
        }),
        new MiniCssExtractPlugin({ // 添加插件
            filename: '[name].[hash:8].css'
        }),
        new webpack.HotModuleReplacementPlugin()
    ],
...

 use: [
     MiniCssExtractPlugin.loader,//将处理好的 css 通过 style 标签的形式添加到页面上
     'css-loader',//将 CSS 转化成 webpack 能够识别的数据
     'postcss-loader',//自动添加 CSS3 部分属性的浏览器前缀
     'less-loader',// 将 less 文件转化为 css
 ],

....

此时运行 npm run build。则可以在 dist->index.html 文件夹下看到 css 文件的引入了。

ES6 构建

尽管 ES6 的语法在开发中已经较为广泛的被使用,但并不是每个浏览器都能识别。

Babel 的出现解决了这个问题。

官网说明:

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

如:

// Babel 输入: ES2015 箭头函数
[1, 2, 3].map(n => n + 1);

// Babel 输出: ES5 语法实现的同等功能
[1, 2, 3].map(function(n) {
  return n + 1;
});

我们可以看到,他的转换还有一个 缺陷, map 等方法并不会进行转换,因此我们需要:

通过 Polyfill 方式在目标环境中添加缺失的特性 (通过引入第三方 polyfill 模块,例如 core-js

因此,我们需要安装:

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

这时,我们在 src/index.js 文件下写入:

let arr = ['--星期一--','--星期二--','--星期三--','--星期四--','--星期五--','--星期六--','--星期日--',]

arr.forEach(element => {
    console.log(element);
});

将 webpack.config.js 中的 mode 改为 ’none‘, 然后直接运行 npm run build。

可以在 dist 中 bundle.js 文件夹下看到:

3.png

并没有发生改变。

然后,我们在 webpack.config.js 中的 module 下的 rules 添加:

{
                test: /\.js$/,
                use:
                 [{
                    loader:'babel-loader',
                    options: {
                        presets: [
                          '@babel/preset-env'// babel 启动插件
                        ],
                      }
                }],
            },

运行 npm run build。

此时我们发现,bundle.js 的图片变成了如下:

4.png

将箭头函数改为了普通函数。

polyfill 暂时无法验证,所以不用。

支持 React 开发

首先安装 react 相关包

npm i react react-dom

Babel通过@babel/preset-react包来构建React应用

npm i @babel/preset-react -D

然后在 webpack.config.js 中对 js 的配置稍作改动,注意,这里要将 mode 改回为 ‘development’ 否则会报错。

{
                test: /\.(jsx|js)$/,
                use:
                 [{
                    loader:'babel-loader',
                }],
            },

你可能发现了,配置里的 preset 选项不见了。为了避免页面冗余,我新建了一个文件 :.babelrc, 在这个文件中写入了 预设配置。

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

此时配置完毕,然我们来进行验证吧!

新建 app.jsx 文件,写入熟悉方便的 react 代码。

import React from 'react';

export default class App extends React.Component{

    handleClick = () =>{
        console.log('click!')
    }

    render(){
        return <div onClick={this.handleClick}>ClickTest</div>
    }
}

然后删除 index.js 原有的代码,写入:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app.jsx';
import "./index.less"

ReactDOM.render(<App />,document.querySelector("#root"))

此时已经没有必要用 showDate.js 了,因此我删除了 showDate.js

目前我的目录结构如下:

5.png

然后 npm run dev 运行项目,可以看到界面上出现了 ClickTest ,点击即可在控制台进行对应输出。

提高开发效率的一些配置- Resove

alias -- 让导入模块变得更简单

用 react 开发的时候,我经常会写一些组件,放在 src/component 文件夹下。

在进行页面开发时,如果页面层级过深,导入的时候则非常容易出错。使用 resove 中的 alias 可以解决这个问题。

现在 src/component 文件,在文件下创建 MyDiv 文件夹及 index.jsx 文件,写入如下代码:

import React from 'react';
import "./index.less"

export default class App extends React.Component{
    render(){
        return <div className='my-div'></div>
    }
}

创建 index.less 文件,写入如下代码:

.my-div{
    width: 200px;
    height:200px;
    border:1px solid rgb(228, 218, 217);
    margin:20px auto;
}

此时,如果直接引入改组件,则在 src/app.jsx 写入如下代码:

import React from 'react';
import MyDiv from './component/MyDiv/index.jsx'

export default class Index extends React.Component{

    handleClick = () =>{
        console.log('click!')
    }

    render(){
        return <div onClick={this.handleClick}>
            <MyDiv></MyDiv>
        </div>
    }
}

此时,如果我们在 webpack.config.js 添加如下配置:

resolve:{
        alias:{
            '@component': path.resolve(__dirname, 'src/component'),
        }
    },

导入 MyDiv 的语句则可以变成下面这样:

import MyDiv from '@component/MyDiv/index.jsx'

当文件层级较深的时候,这种写法会方便很多。

extensions -- 不用再写拓展名

添加以下配置:

...
resolve:{
        alias:{
            '@component': path.resolve(__dirname, 'src/component'),
        },
        extensions:['.js','.jsx'],
    },
...

配置完这个后,webpack 会读取配置指定的拓展名尝试访问文件是否存在。没有配置这个之前,当 访问 MyDiv 时,默认访问 index.js,所以必须指定 MyDiv/index.jsx。

配置完这个后,如果 index.js 不存在,它会继续找 index.jsx 。这样引入的时候我们可以写成:

import MyDiv from '@component/MyDiv'

看着简洁清晰了许多。

像 antd 一样引入自己的组件

如果我们组件写的比较多,引入多个组件,要是每个组件都占一行,未免有些冗余。

我们可以这样做,在 component 下新建 index.js 文件。

我已经重新写入了一个 MyButton 组件,在此不再累述。、

写入:

export { default as MyDiv } from './MyDiv';
export { default as MyButton}  from './MyButton';

此时我们就可以像这样在 app.jsx 中引入组件了:

import { MyDiv, MyButton } from '@component';

是不是看起来更加直观、整洁了呢?

避免 Class 名重复 - CSS Modules

完成以上工作后,我发现我两个组件中的样式有点串。

原来,我忘记修改 复制文件的 index.less 文件中的 class 名,而直接修改了样式,它们打包到一个 css 文件夹下,所以两个样式都对原组件生效了。

这可不行!!!总会有偷懒不想写到公共样式文件的时候,总会有复制粘贴的时候!

这个时候怎么办呢?

开启 css-loader 的 modules, 它会为每个 class 生成唯一标识。

文章发表后才发现问题的,所以后续截图有误差,实际的 css-loader 需要添加 modules.

{
                test: /.less$/,
                use: [
                    MiniCssExtractPlugin.loader,//将处理好的 css 通过 style 标签的形式添加到页面上
                    {
                        loader:'css-loader',
                        options: {
                             modules: true
                        }
                    },//将 CSS 转化成 webpack 能够识别的数据
                    'postcss-loader',//自动添加 CSS3 部分属性的浏览器前缀
                    'less-loader',// 将 less 文件转化为 css
                    {
                        loader: 'thread-loader', // 开启多进程打包
                        options: {
                          worker: 3,
                        }
                      }
                ],
                include:[path.resolve(__dirname, 'src')]
            },

图片处理

新建 src-assets 文件夹,放入一张图片。

配置别名:

...
alias:{
    '@component': path.resolve(__dirname, 'src/component'),
     '@assets': path.resolve(__dirname, 'src/assets'),
},
...

我们可以尝试一下,直接引入图片会报错。

因此我们需要下载:

npm install file-loader -D

配置 webpack.config.js

...
{
    test: /\.(jpe?g|png|gif)$/i, // 匹配图片文件
    use:[
        {
            loader:'file-loader',
            options: {
                limit: 10240,//如果文件小于限制的大小,则会返回 base64 编码
            }// 使用 file-loader
        }
    ]
}
      
...

然后就可以正常显示图片了。

结语

到这里,一个基于 React + less + es6 的项目搭建就基本完成了。

但是这远远没有触及到 webpack 的核心功能,及项目的性能优化。

下篇文章,我们继续。