Typescript + React + Webpack4(2)

在上一篇我们讲了Typescript + React + Webpack4的基本配置,通过在上一篇的配置,我们能够:
1:通过npm run dev在浏览器里面看到一个只包含html的页面
2:通过npm run build打包可用于上线的文件
但是,在一个完整的前端项目里,以上的配置满足了大体的框架,但是还有很多细节需要补充

1:SCSS相关配置

1.1:安装node-sass, sass-loader, css-loader, style-loader

npm install --save-dev node-sass sass-loader css-loader style-loader

node-sass - 把SCSS编译为CSS(被sass-loader使用)
sass-loader - 把SCSS编译为CSS
css-loader- 使得@import 和 url() 可以工作
style-loader- 通过使用<style>把css添加到HTML文件的<head>里面,从而使css生效

1.2: SCSS的webpack相关配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    devtool: "source-map",
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.scss']
    },
    entry: {
        main: './src/index.tsx'
    },
    output: {
        path: path.join(__dirname, '/dist'),
        filename: "bundle.min.js"
    },
    module: {
        rules: [
            {
                test: /\.ts(x?)$/,
                exclude: /node_moduls/,
                use: [
                    {
                        loader: "awesome-typescript-loader"
                    }
                ]
            },
            {
                enforce: "pre",
                test: /\.js$/,
                loader: 'source-map-loader'
            },
            {
                test: /\.scss/,
                use: ['style-loader', 'css-loader', 'sass-loader']
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ]
}

要使得SCSS能正确工作,我们修改了2个地方,
1 : 一个是在resolve:{extensions: [ ]}里面添加了'.scss'
2: 在module:rules:[ ]里面添加了跟css相关的loader:

{
       test: /\.scss/,
       use: ['style-loader', 'css-loader', 'sass-loader']
}

注意:
1:这里我们没有添加node-sass,因为实际上是sass-loader来负责把SCSS编译为CSS,而node-sass又是sass-loader的依赖,所以我们需要安装它。
2:['style-loader', 'css-loader', 'sass-loader']这三个loader的顺序是重要的,必须按照上面的代码示例里面的那样,不然会收到编译错误。

2:Image相关配置

2.1 安装file-loader

npm install --save-dev file-laoder

file-laoder用来识别使用了import/require()的文件,把之转化为一个url,并且打包之后放到output目录下

2.2 配置file-loader

在module:rules:[ ]里面添加跟file-loader相关的配置:

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

2.3 添加图片文件,编写使用图片代码

首先看一下我们src下面的结构:


Screen Shot 2019-11-25 at 4.55.12 PM.png

我们可以看到当我们使用import 是引入一个图片的时候,报错了,如果鼠标hover上去或者重新build以下,我们会得到一个错误:

TS2307: Cannot find module './images/moon.jpg'.

这里报的错是TS2307,是Typescript的一个错。这个错误的原因是当使用import去引入一张图片的时候,Typescript并不能识别图片文件。
怎么办呢?解决办法有2个:
1:当引入图片文件的时候,使用require而不使用import
2:为图片文件编写.d.ts文件
但是,在Typescript环境下使用require,需要先安装@types/requirejs,不然之后TS又会抱怨找不到require

npm install --save-dev @types/requirejs

2: 修改代码
把之前的

import Moon from './images/moon.jpg'

改为:

const Moon = require('./images/moon.jpg');

这个时候,我们能在页面上看到我们的图片了。但是!,我们依然会得到TS的error:

ERROR in [at-loader] ./node_modules/@types/requirejs/index.d.ts:422:13
    TS2403: Subsequent variable declarations must have the same type.  Variable 'require' must be of type 'NodeRequire', but here has type 'Require'.

我们可以看到这个error是来自于我们刚刚安装的./node_modules/@types/requirejs。对于我们安装的第三方库,我们无法去修改它的代码。
所以,一个最简单的解决这个问题的办法就是不要使用@types/requirejs!而安装@types/webpack-env, 并且把我们之前安装的@types/requirejs给去掉。安装了@types/webpack-env,你依然可以使用require,并且@types/webpack-env本身不会报错:

npm install --save-dev @types/webpack-env
npm uninstall @types/requirejs

好的,现在你不会得到任何错误,并且愉快地使用require( )来加载你的图片了。

3:打包优化

3.1 使用code splitting

wepack.config.json

output: {
        path: path.join(__dirname, '/dist'),
        filename: "[name].[contenthash].js"
    }

当我们使用了contenthash,打包出来的文件会根据代码是否变化了而在文件名字里面出现不同的hash值。这样的好处就是,强迫浏览器去服务器下载最新的文件,而不让用户还使用老文件。
我们在命令行里面使用了以下命令后会得到下面的文件:

npm run build
Screen Shot 2019-11-25 at 7.17.36 PM.png

大家可以从截图上看到,打包之后的文件里面有一个js文件:main.27c4cc56e2d346938472.js,它的大小是132 KiB。

但是,以上的打包方式存在一个问题。我们的代码是由两部分组成:我们自己写的业务代码 + 依赖的第三方库(例如react,react-dom等)。我们的业务代码是经常变化的,所以当我们的业务代码有变化时,我们需要重新打包,从而得到一个不同hash值的main.xxx.js文件,假如它是200KB,那么用户得重新下载200KB的代码。但是,这200KB里面,其实包含了不会变化的第三方依赖库代码(假如是50KB),这部分如果可以被浏览器缓存起来,那么用户便不会再需要重新下载200KB,而只是业务那部分的150KB。

那么假如,我们有办法把这部分变化的(业务代码)和不变的(第三方依赖)打包成2个文件,那就可以解决以上问题了。事实上,webpack 4里面,你可以通过splitChunks来实现:
webpack.config.json

module.exports = {
    //  其他的配置项省略了
    optimization: {
        splitChunks: {
            chunks: "all"
        }
    }
}

添加上面的配置项之后,我们再来跑npm run build,得到以下结果:


Screen Shot 2019-11-25 at 7.22.10 PM.png

对比之前,我们可以看到现在我们多了一个vendors~main.9c44b1b7bc198b90e63f.js文件,而现在的main.94e269bacef895deeefb.js文件大小就只有2.37 KiB了。

3.2 把css从js文件里面抽离出来

大家可能注意到了,我们之前明明添加了一个App.scss文件,但是我们打包出来的文件却没有css文件。因为我们把css代码也都打包到main.xxx.js文件里面去了。但是,更好的做法是把css和js分来。为了实现这个目的,我们需要用到:mini-css-extract-plugin。首先,安装:

npm install --save-dev mini-css-extract-plugin

现在我们已经安装好了,在进行我们的config之前,有必要先了解一下mini-css-extract-plugin的用法。mini-css-extract-plugin应该只能被用在production环境,且此时不应该使用style-loader。
所以,这里我们有个需求,我们需要在webpack.config.js文件里面拿到当前的mode,从而知道是在development环境还是production环境。为了达到这个目的,我们的webpack.config.js需要export一个function而不是object,从而在function里面通过argv拿到mode。所以我们的webpack.config.js变成了:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env, argv)=>{
    const isDevMode = argv.mode === 'development';
    return {
        devtool: "source-map",
        resolve: {
            extensions: ['.ts', '.tsx', '.js', '.scss']
        },
        entry: {
            main: './src/index.tsx'
        },
        output: {
            path: path.join(__dirname, '/dist'),
            filename: isDevMode ? "[name].[hash].js" : "[name].[contenthash].js"
        },
        module: {
            rules: [
                {
                    test: /\.ts(x?)$/,
                    exclude: /node_moduls/,
                    use: [
                        {
                            loader: "awesome-typescript-loader"
                        }
                    ]
                },
                {
                    enforce: "pre",
                    test: /\.js$/,
                    loader: 'source-map-loader'
                },
                {
                    test: /\.scss/,
                    use: [
                        {
                            loader: MiniCssExtractPlugin.loader,
                        },
                        'css-loader',
                        'sass-loader']
                },
                {
                    test: /\.(png|svg|jpg|gif)$/,
                    use: [
                        'file-loader',
                    ],
                },
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: "./src/index.html"
            }),
            new CleanWebpackPlugin(),
            new MiniCssExtractPlugin({
                filename: isDevMode ? '[name].css' : '[name].[contenthash].css',
                chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css'
            })
        ],
        optimization: {
            splitChunks: {
                chunks: "all"
            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348