别名配置
对于ts+webpack 的「monorepo」项目 别名的配置有 两种
- 第一个是在tsConfig 中的别名配置, 这个配置的好处方便 子项目中 互相引用
- 第二个是在webpack 中配置别名, 为了可以少写很长路径, 同时打包的时候也能找到文件。
如上图 我 在 「3d」 这个项目 我想引用 「utils」 中的方法, 首先这两个项目 分别都
是单独的项目, 都有自己的「tsConfig.json」 文件
为了 防止ts 报错 我们项目根目录的 「tsConfig.json」 配置下别名
"baseUrl": "./packages", // 根路径 路径映射, "paths": { "@fly/util": ["./utils/src/index.ts"] }
BaseUrl 其实就是我们项目中的根目录,然后下面的path 也就是对应 别名,也就是相对于我们根路径的映射了 。
然后可以愉快的开发了,我在下面引入 其中写
的测试方法如图所示:
image-20220508180951145
会报错, 报错的根本原因其实是在于tsCofnig.json 中 include 文件 只 包含了当前目录 src 下的文件, 不包含其他目录, 一种解决办法就是将utlis 里的方法移动到 当前目录的 「src」 下面 不过 不太现实。因为未来有可能 我们这个 util 目录 会抽成SDK,放到其他业务中去。
这里ts 官方给出了 解决方案 就是 我们当前项目 引用 其他项目 , 但是有一个必须条件 就是 引用的项目 tsConfig.json 必须是 可编辑的
也就是 「composite: true」
"references": [ { "path": "./../utils" } ]
这里的引用 其实就是找到另一个项目的「tsConfig.json」
webpack 别名配置
webpack 的别名配置主要是为了让 webpack 在打包的时候能够找到对应的目录 从而防止报错
然后这里的名字最好和 「tsConfig」 中的别名保持统一, 下方的rootPath 其实对应的就是
resolve: { extensions: ['.ts', '.tsx', '.js'], alias: { '@fly/util': path.join(rootPath, 'utils/src'), }, },
html 配置
这里我们这里只是对于ts 的处理,但是我们打包后的代码,需要放到html 文件, 浏览器 才能展示对吧,
这里需要一个plugin 「:html-webpack-plugin」
我们安装 下
yarn add html-webpack-plugin -D -W
我们项目根目录下 创建一个 「public」 目录 然后新建一个 html 文件 然后 导入我们的模板文件 如下:
<!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> </head> <body> <div id='root'></div> </body> </html>
然后我们webpack 进行配置如下图所示:
new HtmlWebPack({ filename: 'index.html', template: path.resolve(__dirname, '../public/index.html'), }) ],
然后我们进行打包 我们会看到 「dist」 目录会 出现 「index.html」 然后会发现 引用了我们打包的 bundle.js 文件 如下图所示:
dev SERVER 配置
配置开发服务器, 这是非常有必要的, 不然 我们每次改一点打包难道构建一次嘛, 那这样也太麻烦了
我们安装
yarn add -D -W webpack-dev-server webpack-merge
我们新建一个 webpack.dev.js 做到了 开发环境的打包 和 生产环境的打包
我们看下配置
// const { merge } = require('webpack-merge'); import webpack from 'webpack' const { merge } = require('webpack-merge') const baseConfig = require('./webpack.base.ts'); const pathRoot = require('path'); const devConfig = { mode: 'development', devServer: { // static允许我们在DevServer下访问该目录的静态资源 // 简单理解来说 当我们启动DevServer时相当于启动了一个本地服务器 // 这个服务器会同时以static-directory目录作为跟路径启动 // 这样的话就可以访问到static/directory下的资源了 static: { directory: pathRoot.join(__dirname, '../public'), }, historyApiFallback: true, // 默认为true hot: true, // 是否开启代码压缩 compress: true, // 启动的端口 port: 9000, }, }as webpack.Configuration ; module.exports = merge(devConfig, baseConfig);
然后我们在package.json 配置 开发环境的脚本
"dev": "webpack serve --config ./scripts/webpack.dev.ts",
然后 「yarn dev」 我们就可以
image-20220515182248590
但是如果我们在实际开发过程 可能会有下面这种场景, 会切换不同的分支,然后 启动项目 进行调试, 这样其实就会有端口号占用,导致本地环境启动失败。
端口占用问题解决
我们安装 「portFinder」 这个npm 包 , webpack 配置 不仅仅可以配置成一个 函数,还可以配置成一个对象。
async function runDev () { try { const port = await portFinder.getPortPromise() devConfig.devServer.port = port return merge(devConfig, baseConfig) } catch(e) { throw new Error(e) } } module.exports = runDev;
css 资源导入
到现在我们已经有 html 文件了, 然后我们就可以引入css,前端对于css 的 处理其实有很多种
- css 预处理器方案 比如 「sass、less」 「可以定义变量 继承 让css 变的可编程」,增强css 的开发体验
- Css Module 主要用来处理 css 样式冲突的问题, 将css 类名 处理成hash 值
- css 后处理器 postcss 这个可以理解为 css 届的「babel」, 比较常见的功能 「px2vw 移动端」 做适配的时候经常用
- 还有 css in js 这个就是在 js 中写css 这个开发体验也很好 css modules 也都支持 社区中 比较有名的库 就是 emotion 和style-components
- 最后就是 比较新的 css 原子化 解决方案 CSS 原子化框架,如
Tailwind CSS
、Windi CSS
,通过类名来指定样式,大大简化了样式写法,提高了样式开发的效率,主要解决了原生 CSS 「开发体验」的问题
我们先在页面创建一个css 文件 index.css 然后引入
css-loader
我们先安装 css-loader
yarn add -D -W css-loader style-loader
Css-loader 的职责 其实是负责css模块化,本身不具备 应用样式 到 dom 节点 中 。
我创建了css 文件, 然后 在 主页引用了 我们看看输出的是什么???
image-20220521210415344
// import React from 'react' import add from '@fly/util' import React, { useEffect, useState } from 'react' import ReactDom from 'react-dom' import style from './index.css' function App() { useEffect(() => { console.error(style) }, []) return <div className={style.title}>我是测试的222 </div> } ReactDom.render( <div> <App /> <div className={style.img} /> </div>, document.getElementById('root'), )
我们打印下 输出内容:
image-20220521210549098
很显然了css-loader, 负责cssModules 处理, 然后同时 生成 对应的 css 类名 , 这里的 话 其实是做了hash处理 ,防止全局样式冲突, 所以, 所以我们一般是需要 style-loader 对样式文件进行处理。
webpack 的配置如下所示:
{ test: /\.css$/, exclude: /node_modules/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', options: { sourceMap: false, modules: { localIdentName: '[path][name]__[local]--[hash:base64:5]', }, }, }, ],
这里我主要讲一下 localIdentName 表示 css 生成的文件 类名字 是带hash 的
我们看下这时候的打包运行结果:
这时候引用的css 文件 会变成 一个 对象, 如图
image-20220521211154950
然后className 就是我们刚才webapck 配置的 我们在看下 style-loader 起到的效果
image-20220521211314772
其实就是在html 文档中 插入 style 标签, 这样我们就会显示css 样式了。但是这样 显然不是我们喜欢要的方式, 我们希望每一个js文件,都有对应的css 文件。因为 这会导致 打包出来的 html 文件 过大, 而且 不支持 按需加载。
mini-css-extract-plugin
这时候我们使用 安装 这个npm 包 mini-css-extract-plugin
yarn add -D -W mini-css-extract-plugin
然后我们webpack 进行下面配置 我们把之前的 「style-loader」 去掉 引入 当前的plugin
我们进行 如「下配置分别在loader 层面 和 plugin 层面」进行配置
❝这里有一点要提醒 就是 我们在开发环境下 生成的css 文件 文件名字就不用hash 了 对于构建 和hmr 不太友好
生产模式下 记得 加 hash 模式 有利于走浏览器缓存, 如果内容没有变化的化, 加载首屏速度
❞
{ test: /\.css$/, exclude: /node_modules/, use: [ { loader: MinCssExtractPlugin.loader, }, { loader: 'css-loader', options: { sourceMap: false, modules: { localIdentName: '[path][name]__[local]--[hash:base64:5]', }, }, }, ], } plugins: [ new HtmlWebPack({ filename: 'index.html', template: path.resolve(__dirname, '../public/index.html'), }), new MinCssExtractPlugin({ filename: '[name].css', }), ],
基础的loader 已经处理完毕, 接下来我们 安装 sass-loader 和 postcss-loader
sass-loader
我们安装sass-loader
yarn add sass-loader
然后 webpack 进行下面配置
{ test: /\.(css|scss)$/, exclude: /node_modules/, use: [ { loader: MinCssExtractPlugin.loader, }, { loader: 'css-loader', options: { sourceMap: false, modules: { localIdentName: '[hash:base64:5]', }, }, }, { loader: 'sass-loader', }, ],
主要是正则匹配 加下路径就OK了
postcss-loader
Postcss 主要是对css 进行 类似于 ast 语法树 处理, 有自己的定义的config, 也可以支持自定义plugin 。这里我们就自己定义plugin配置文件如下:
分别对应下面这几个 「插件」
yarn add -D -W postcss-px-to-viewport autoprefixer cssnano
配置如下:第一个插件 「postcss-px-to-viewport」其实 就是我们在开发过程中, 「px转成 vw 的 主要是兼容移动端」 ,这里的话 ,按照你公司设计的设计稿 进行更改就可以了 ,
第二个插件 「autoprefixer」 浏览器厂商 各种兼容性 适配
第三个插件 「cssnano」 主要是用来压缩css 代码的
module.exports = { plugins: [ require('postcss-px-to-viewport')({ viewportWidth: 375, // (Number) The width of the viewport. // viewportHeight: 1334, // (Number) The height of the viewport. unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to. viewportUnit: 'vw', // (String) Expected units. selectorBlackList: ['.ignore', '.hairlines', 'no-convert'], // (Array) The selectors to ignore and leave as px. minPixelValue: 1, // (Number) Set the minimum pixel value to replace. mediaQuery: false, // (Boolean) Allow px to be converted in media queries. }), require('autoprefixer'), require('cssnano')({ preset: 'default', }), ], }
由于这两个 loader 都对css的 模块化做了处理,所以我们在css-loader 要加一个配置
{ loader: 'css-loader', options: { sourceMap: false, modules: { localIdentName: '[hash:base64:5]', }, importLoaders: 2, }, }, { loader: 'postcss-loader', options: { postcssOptions: { config: path.join(__dirname, '..', './postcss.config.js'), }, }, }, { loader: 'sass-loader', },
增加了一个 importLoaders 的属性。
组件样式按需加载
我们这里 安装 antd 使用他的 buttton 组件 来进行测试一下:
import { Button } from 'antd
然后scss 文件 也要引入 antd 的css 文件样式
@import '~antd/dist/antd.css';
然后我们重新 预览后 发现 button 的组件 样式 不起作用 发现 「button 的class name 在 main.css 文件里面没有」
image-20220522004623648
我这按钮的样式 没有, 因为我们做了css 模块化 所以对每一个类名 做了 hash ,但是div 中是没有的 所以样式 没有应用
image-20220522004824475
我们怎么处理了呢 ??css-loader 生成hash 的时候 只针对 scss 文件, css 文件 不做处理
{ loader: 'css-loader', options: { sourceMap: false, modules: { auto: (resourcePath) => resourcePath.endsWith('.scss'), localIdentName: '[hash:base64:5]', }, importLoaders: 2, }, },
第一个问题解决了, 样式能够正常显示了, 但是这样会有一个 问题就是 css 样式 我们把antd 的所有样式都引进来了, 我其实只需要某些组件的样式。css 文件引入的太大了, 我们需要 优化,这里的话 我们webpack 单独 对css 文件的 重新写配置,
// 这个是用来处理 组件按需加载样式的 { test: /\.css$/i, use: [ { loader: MinCssExtractPlugin.loader, }, { loader: 'css-loader', options: { modules: false, }, }, ], },
「并且这里我是禁用了的css 模块化的,一方面可以提高性能禁用webpack cssModule的一些特性, 另一方面呢可以 是因为 对于外部引进来的 组件 ,我们是不需要对类名 进行hash 处理的, 因为有可能 我们需要对样式进行修改, 然后以选择器 权重的 优先级 大于他, 进行样式覆盖。」
我们看下我怎么如何进行配置, 这里主要用到了 babel-plugin-import 这个插件 这个插件做了什么事, 其实就是做了下面这件事,而且 antd 本身支持 esm , 更好的有利于 tree-shaking
import { Button } from 'antd'; ↓ ↓ ↓ ↓ ↓ ↓ var _button = require('antd/lib/button'); require('antd/lib/button/style');
我们安装 安装 babel-plugin-import
yarn add babel-plugin-import -D -W
然后 我们在babel.config.js 里面配置 如下
[ 'import', { libraryName: 'antd', libraryDirectory: 'es', style: 'css', }, 'antd', ],
然后这里的 style 根据你项目中 用的 是 less 还是 scss 灵活配置, 默认 他是拿less 的 文件的 。