17. 扩展功能

169 阅读4分钟

一、PostCSS 与 CSS 模块

相关代码

PostCSS 是一个用 JavaScript 工具和插件转换 CSS 代码的工具。比如可以使用 Autoprefixer 插件自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮我们自动地为 CSS 规则添加前缀,将最新的 CSS 语法转换成大多数浏览器都能理解的语法。

CSS 模块 能让你永远不用担心命名太大众化而造成冲突,只要用最有意义的名字就行了。

1. PostCSS

在 Webpack 中使用 PostCSS,需要安装 style-loadercss-loaderpostcss-loader

npm i style-loader css-loader postcss-loader -D
// webpack.config.js

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                exclude: /node_modules/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            }
        ]
    }
}

创建 postcss.config.js:

// postcss.config.js

module.exports = {
    plugins: [
        require('autoprefixer'),
        require('postcss-nested')
    ]
}
  • 插件 autoprefixer 提供自动给样式加前缀去兼容浏览器
  • 插件 postcss-nested 提供编写嵌套的样式语法
// package.json

"browserslist": [
    "> 1%",
    "last 2 versions"
]
  • last 2 versions:表示每个浏览器中最新的两个版本;
  • > 1% 或者 >= 1%:表示全球浏览器使用率大于 1% 或大于等于 1%。

2. CSS 模块

使用 CSS 模块可以解决多人编写的样式可能会冲突的问题。

// webpack.config.js

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                exclude: /node_modules/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: { 
                            modules: true    // 开启 css 模块
                        }
                    },
                    'postcss-loader'
                ]
            }
        ]
    }
}

假如有样式文件:

/* style.css */

body {
    display: flex;
    flex-direction: column;
    .box {
        width: 100px;
        height: 100px;
        background: red;
    }
}

在 js 文件里导入 css 文件:

import style from './style.css'    // 开启 css 模块后,可以导入模块

const div = document.createElement('div')
div.textContent = 'hello webpack'
div.classList.add(style.box)       // style 里可以识别 class 样式
document.body.appendChild(div)

二、Web Works

相关代码

有时我们需要在客户端进行大量的运算,但又不想让它阻塞我们的 js 主线程,你可能第一时间考虑到的是异步。但事实上,运算量过大(执行时间过长)的异步也会阻塞 js 事件循环,甚至会导致浏览器假死状态。

这时,HTML5 的新特性 WebWorker 就派上了用场。

HTML5 以前,打开一个常规的网页,浏览器一般至少存在三个线程(公用线程不计入在内):分别是 js 引擎线程(处理 js)、GUI 渲染线程(渲染页面)、浏览器事件触发线程(控制交互)。

当一段 JS 脚本长时间占用着处理机,就会挂起浏览器的 GUI 更新,而后面的事件响应也被排在队列中得不到处理,从而造成了浏览器被锁定进入假死状态。

现在如果遇到了这种情况,我们可以做的不仅仅是优化代码,还可以使用 HTML5 提供的 WebWorker。

WebWorker 提供了 js 的后台处理线程的 API,它允许将复杂耗时的单纯 js 逻辑处理放在浏览器后台线程中进行处理,让 js 线程不阻塞 UI 线程的渲染,多个线程间可以通过相同的方法进行数据传递。

// new Worker(scriptURL: string | URL, options?: WorkerOptions)
new Worker("someWorker.js")

也就是说,需要单独写一个 js 脚本,然后使用 new Worker 来创建一个 Work 线程实例。这意味着并不是将这个脚本当做一个模块引入进来,而是单独开一个线程去执行这个脚本。

常规模式下,Webpack 只会打包出一个 bundle.js,那我们的 worker 脚本怎么办?Webpack4 的时候就提供了 worker-loader 专门配置 WebWorker,但在 Webpack5 中,已经内置了这个功能。

示例:

// work.js

self.onmessage = (message) => {
    self.postMessage({
        answer: 1111
    })
}
// app.js

// import.meta.url 参数能够锁定当前的这个模块,注意,它不能在 CommonJS 中使用
const worker = new Worker(new URL('./work.js', import.meta.url))

worker.postMessage({
    question: 'hi,那边的workder线程,请告诉我今天的幸运数字是多少?'
})

worker.onmessage = (message) => {
    console.log(message.data.answer)
}
// webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode: 'development',
    entry: './src/app.js',
    plugins: [
        new HtmlWebpackPlugin()
    ]
}

运行 npx webpack,发现 dist 目录中除了 main.js,还有一个 src_work_js.js,这说明 Webpack5 自动的将被 new Worker() 使用的脚本单独打出了一个 bundle。

image.png

三、TypeScript

相关代码

1. 在 Webpack 工程化环境中集成 TS

  1. 安装 typescriptts-loader
npm install --save-dev typescript ts-loader
  1. 添加 ts 配置文件(tsconfig.json):
npx tsc --init
  1. 生成的 tsconfig.json 中注释了很多配置,可以根据想要的效果打开对应的配置,如:
{
    "compilerOptions": {
        "target": "es2016",
        "module": "commonjs",
        "rootDir": "./src",
        "outDir": "./dist",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
    }
}
  1. 新增一个 src/app.ts:
// app.ts

const age: Number = 18
console.log(age)
  1. 配置 webpack.config.js:
// webpack.config.js

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

module.exports = {
    mode: 'development',
    entry: './src/app.ts',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist')
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                exclude: /node_modules/,
                use: 'ts-loader'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin()
    ],
    devtool: 'inline-source-map',
    resolve: {
        extensions: ['.ts', '.js']
    },
}

2. 在 TypeScript 中使用第三方类库

安装第三方库时,要同时安装这个库的类型声明文件。以 lodash 为例:

npm i lodash
npm i @types/lodash --save-dev

可以从 Type Search 中找到对应的类型声明文件。