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

231 阅读9分钟

我简单吐槽两句前端开发的问题

我在 前端自动化构建,解放我的双手 一文中,通过外部插件和命令,简单的完成了使用 less 进行开发、浏览器自动刷新 这两部分工作,可是在实际开发中,这远远不够啊!问题还很多!


就只论前端开发,问题有:

我常用 ES6 语法,但是有的浏览器并不支持。能不能把我写的代码,自动转化为浏览器支持的 JS 语法呢?

TS 转化成 JS 怎么办?

实际部署的过程中,静态资源(图片,css 等) 可能会部署到静态文件服务器下,这时候资源的引入路径难道手动挨个调吗?

有些比较小的图片我想让它转化成 base64 格式,这个不会要我挨个去判断吧?

还有一些 js的引入,有时候复制粘贴难免会写错,浏览器确依然下载,造成资源浪费。不能让我一个个文件去排查吧?

为了减少下载时间,还想压缩一下代码,手动压缩,这……


要是说到数据对接,问题有:

必须得得到后端接口完备且运行正常,才能做数据测试,这也太浪费时间了吧!


说到部署,问题有:<待完善>

无法简洁的指定部署内容的目录地址。

无法通过阅读代码确定文件位置。


这些需求,如果按照我上文中的、挨个寻找、安装对应插件,然后按照顺序执行命令,

多少有点麻烦(上文中,我找监听 less 文件的命令就用了一点时间)

而且 pakage.json 文件中的命令不直观,写法上有差异,不利于维护,容易出错。

项目复杂度低,不太了解打包场景。什么时候需要用 webpack 进行打包?


这么多问题,有没有好的解决方案?

有的。市面上针对这些问题,出现了多款工具。今天我想使用的,也是我较为常用的 webpack.

webpack 踏着七彩祥云出现了

严格来说,webpack 并不是一款构建工具,而是一款打包工具。

它的官网介绍如下:

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

构建

但是,webpack 提供了 loader, 能够让我们在打包之前,自定义匹配规则,将源代码进行转换,比如将 less 转化为 css,将 ts 转化为 js

webpack 也提供了了 plugins 来对构建流程进行操作。在我上一篇文章中,必须先完成 less 的转化 才能 刷新浏览器,这种先后执行顺序的指定,就可以在 plugin 中完成。

此外, ReactVue 等较为流行的框架对应的 webpack 均是由官方或者作者本人编写的,因此相对于其他工具而言,webpack 具有一定的优势。

打包

webpack 也有个缺点,从它的描述中 静态模块打包器,可以看出,它只支持模块化的开发。

简而言之,模块化开发就是将一个 js 文件当作一个模块,有独立的作用域,能够向外部暴露允许的变量、函数和类;能够避免传统的 script 引入带来的命名空间冲突问题,能够更加灵活的控制它的引入位置。

这样的开发模式使用 webpack 打包后,会将用到的资源打包到一个文件,页面中只加载一个 JS 文件,相比于传统的多次请求多个 JS 的模式来说,能够节省网络开销。

webpack 的打包结果也分两种:

  • 将存在依赖关系的模块按照特定规则合并为单个js文件,一次全部加载进页面中
  • 在页面初始时加载一个入口模块,其他模块异步地进行加载

很明显,第二种方案能够减少 js 文件的加载时间,优化用户体验。

webpack 提供了完备的 代码分割解决方案。即,它可以按需打包多个 js 文件,首屏只加载必要部分,不太中药的功能放到后面动态的加载。

webpack 的基本使用

首先,我们安装 webpack .

新建文件夹 project3, 进入终端,运行 npm init 初始化项目。

安装 webpackwebpack-cli

npm install webpack webpack-cli -D

由于接下来要完成的任务较多,我们新建 webpack.config.js, 并在 package.json 中添加运行命令。

"start":"webpack --config webpack.config.js"

打包验证

现在,我们目的是验证 webpack 是否能够将依赖模块打包到 一个 js 中。

建立如下目录结构:

1.png

showDate.js 中写入

export default function(){
    document.write(new Date())
}

index.js 中写入

import showDate from './showDate';

document.write('Webpack');
showDate();

然后我们在 webpack.config.js 写入如下内容:

const path = require('path');

module.exports = {
    mode:'development',// 开发模式
    entry:'./src/index.js',// 入口文件配置。默认入口文件为
    output:{
        filename:'bundle.js',// 打包后的文件名
        path:path.resolve(__dirname,'./dist')// 打包结果目录名
    }
}

运行 npm run start

可以看到 dist 下 生成了 bundle.js 文件。

此时,只要在 index.html 中引入 bundle.js 文件,然后在浏览器中打开 index.html,可以看到,src 下的文件都生效了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./dist/bundle.js"></script>
</head>
<body>
    <div id="root">
    	learn webpack
    </div>
</body>
</html>

起一个本地服务器 - devServer

使用 webpack 怎么完成将项目放到本地服务器上、并自动刷新呢?

我们使用 webpack 社区提供的本地开发工具 webpack-dev-server。它不仅能够调动 webpack 进行模块打包,还能作为本地服务器使用,处理静态资源文件请求。同时它具有 live reloading(实时重新加载) 功能。

npm install webpack-dev-server --save-dev 

本地开发环境中用到,所以使用 --dev。工程上线时进行依赖安装,可以通过 npm install --production 过滤 devDependencies 中的冗余模块。

package.json中添加一个 dev 指令。

"dev":"webpack-dev-server"

然后在 webpack.config.js 配置

devServer:{
        static: {
            directory: path.join(__dirname, './dist'),// dist 目录作为服务器使用
          },
        compress: true,// 压缩
        port: 9000,// 端口号
        open:true,// 打包完之后自动打开默认浏览器
    }// devServer 用于放置 webpack-dev-server 配置

此时 dist 目录成为了静态服务器的目录地址。当我们访问服务器时,它会返回静态资源。

如果此时将 index.html 复制到 dist 目录下,修改 js 引入路径,使用 npm run dev 启动,访问 http://localhost:9000 则可以看到展示内容与本地打开的一致。

但是显然,这样并不合理。dist 目录下的文件下是我们最终要部署上去的文件夹,我们开发则是正常的项目目录进行开发。

怎么做才能够让 index.html 文件在编译后自动跑到 dist 目录下,然后自动修改引入路径呢?

修改资源引入路径,生成新的文件到dist目录下-plugin

清空 dist 文件夹。

安装插件

npm install html-webpack-plugin -D

添加配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    mode:'development',
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:path.resolve(__dirname,'./dist')
    },
    plugins:[ // 配置插件
        new HtmlWebpackPlugin({
            template: './index.html',
            scriptLoading: 'blocking',// 默认是 defer.
        })
    ],
    devServer:{
        static: {
            directory: path.join(__dirname, './dist'),
          },
        compress: true,
        port: 9000,
    }
}

使用 npm run dev 启动。访问 http://localhost:9000 则可以看到 index.html 文件中的内容。

2.png

这个时候我们会发现有个问题:dist 文件下没有产生任何内容。

Webpack开发和使用webpack-dev-server有一个很大的区别,前者每次都会生成budnle.js,而webpack-dev-server只是将打包结果放在内存中,并不会写入实际的bundle.js,在每次webpack-dev-server接收到请求时都只是将内存中的打包结果返回给浏览器。

我们可以使用 npm run start,调用 webpack 命令,这时候我们可以发现 dist 文件夹下生成了对应文件。

HTML|JS 热更新

js 实时更新

webpack.config.js 顶部引入

const webpack = require('webpack');

配置 plugins

plugins:[ // 配置插件
        new HtmlWebpackPlugin({
            template: './index.html',
            scriptLoading: 'blocking',
        }),
        new webpack.HotModuleReplacementPlugin()// 添加这一行
    ],

此时,修改 js 文件,即可看到页面即时完成了刷新。


这个时候我发现了一个问题,即我修改 js 文件能够进行热更新,但是修改 html 文件不会。

为什么呢?

webpack需要监听文件的变化才能进行相应的更新。而监听文件是从webpack配置的入口出发,从而对各个依赖进行更新。而入口的js文件一般不会引入html文件作为依赖,所以不会对html文件进行监听,也就不会对html文件进行热更新。 --- 参考

所以我们需要指定监听文件的路径。

修改 devServer 配置如下:

devServer:{
        static: {
            directory: path.join(__dirname, './dist'),
          },
        compress: true,
        port: 9000,
        hot: true, 
        open:true,
        watchFiles:'./index.html'
    }

此时,我们无论是更新 html 还是更新 js ,浏览器都会自动刷新。

less 构建

安装插件

npm install less
npm install less-loader --save-dev 
npn install css-loader -D
npm install style-loader -D
npm install postcss-loader postcss -D // 添加 CSS3 部分属性的浏览器前缀

webpack.config.js 中添加:

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

注意,每个 loader 都有它的功能,webpack 读取数据时从左向右读取,因此,注意 loader 顺序,否则可能会报错。

src 下 添加 index.less 文件夹,写入

@bg-color:rgb(216, 154, 172);
@text-primary-color:rgb(84, 177, 100);
@text-pos:center;

body{
    background-color: @bg-color;
    color:@text-primary-color;
    text-align: @text-pos;
}
  

重新运行,写完 less 之后,浏览器就会自动刷新了。

下篇预告

至此,我们基本已经用 webpack 实现了上篇文章所说的功能,但它能做到的远不止这些。

由于不想写的太冗长,因此我决定分成几篇来写。

下一篇,我将引入 react,进一步完成配置。