从0到1带你用webpack 5构建monorepo项目——上篇(二)

简介: 别名配置对于ts+webpack 的「monorepo」项目 别名的配置有 两种1. 第一个是在tsConfig 中的别名配置, 这个配置的好处方便 子项目中 互相引用2. 第二个是在webpack 中配置别名, 为了可以少写很长路径, 同时打包的时候也能找到文件。1. 如上图 我 在 「3d」 这个项目 我想引用 「utils」 中的方法, 首先这两个项目 分别都是单独的项目, 都有自己的「tsConfig.json」 文件为了 防止ts 报错 我们项目根目录的 「tsConfig.json」 配置下别名"baseUrl": "./packages", // 根路径 路径

别名配置



对于ts+webpack 的「monorepo」项目 别名的配置有 两种


  1. 第一个是在tsConfig 中的别名配置, 这个配置的好处方便 子项目中 互相引用


  1. 第二个是在webpack 中配置别名, 为了可以少写很长路径, 同时打包的时候也能找到文件。


image.png



如上图 我 在 「3d」 这个项目 我想引用 「utils」  中的方法, 首先这两个项目 分别都


是单独的项目, 都有自己的「tsConfig.json」 文件


为了 防止ts 报错 我们项目根目录的 「tsConfig.json」  配置下别名


"baseUrl": "./packages", // 根路径 路径映射,
  "paths": {
    "@fly/util": ["./utils/src/index.ts"]
  }


BaseUrl 其实就是我们项目中的根目录,然后下面的path 也就是对应 别名,也就是相对于我们根路径的映射了 。


然后可以愉快的开发了,我在下面引入 其中写




的测试方法如图所示:

image.png

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  文件 如下图所示:


image.png


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.png


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 的 处理其实有很多种


  1. css 预处理器方案  比如 「sass、less」   「可以定义变量 继承 让css 变的可编程」,增强css 的开发体验


  1. Css Module 主要用来处理 css 样式冲突的问题, 将css 类名  处理成hash 值


  1. css 后处理器 postcss 这个可以理解为 css 届的「babel」, 比较常见的功能 「px2vw 移动端」 做适配的时候经常用


  1. 还有 css in js   这个就是在 js 中写css 这个开发体验也很好  css modules 也都支持 社区中 比较有名的库 就是 emotion 和style-components


  1. 最后就是 比较新的 css 原子化 解决方案 CSS 原子化框架,如Tailwind CSSWindi CSS,通过类名来指定样式,大大简化了样式写法,提高了样式开发的效率,主要解决了原生 CSS 「开发体验」的问题


我们先在页面创建一个css 文件  index.css 然后引入


css-loader


我们先安装 css-loader


yarn add -D -W css-loader  style-loader


Css-loader 的职责 其实是负责css模块化,本身不具备 应用样式 到  dom 节点 中 。

我创建了css 文件, 然后 在 主页引用了 我们看看输出的是什么???


image.png


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.pngimage-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.png


image-20220521211154950


然后className 就是我们刚才webapck 配置的 我们在看下 style-loader 起到的效果

image.png

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.png


image-20220522004623648


我这按钮的样式 没有, 因为我们做了css 模块化 所以对每一个类名 做了  hash ,但是div  中是没有的 所以样式 没有应用


image.png


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 的 文件的 。

相关文章
|
1月前
|
缓存 监控
webpack 提高构建速度的方式
【10月更文挑战第23天】需要根据项目的具体情况和需求,综合运用这些方法,不断进行优化和改进,以达到最佳的构建速度和效果。同时,随着项目的发展和变化,还需要持续关注和调整构建速度的相关措施,以适应不断变化的需求。
|
1月前
|
前端开发 JavaScript
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
|
1月前
|
存储 缓存 前端开发
利用 Webpack 5 的持久化缓存来提高构建效率
【10月更文挑战第23天】利用 Webpack 5 的持久化缓存是提高构建效率的有效手段。通过合理的配置和管理,我们可以充分发挥缓存的优势,为项目的构建和开发带来更大的便利和效率提升。你可以根据项目的实际情况,结合以上步骤和方法,进一步优化和完善利用持久化缓存的策略,以达到最佳的构建效果。同时,不断探索和实践新的方法和技术,以适应不断变化的前端开发环境和需求。
|
4月前
webpack——通过webpack-bundle-analyzer分析项目包占比情况
webpack——通过webpack-bundle-analyzer分析项目包占比情况
43 2
webpack——通过webpack-bundle-analyzer分析项目包占比情况
|
4月前
|
缓存 JSON JavaScript
简单介绍下从零搭建 Webpack 项目
本文详细介绍了Webpack中Loader的概念及其重要性。Webpack仅支持处理JS和JSON文件,而Loader能够帮助处理其他类型的文件,如CSS、图片等,并将其转换为有效的模块。文章首先解释了Loader的基本原理,接着介绍了几种常见Loader的配置和使用方法
27 1
|
4月前
|
前端开发 JavaScript API
|
4月前
|
前端开发 JavaScript C++
【绝技大公开】Webpack VS Rollup:一场前端工程化领域的巅峰对决,谁能笑到最后?——揭秘两大构建神器背后的秘密与奇迹!
【8月更文挑战第12天】随着前端技术的发展,模块化与自动化构建成为标准实践。Webpack与Rollup作为主流构建工具,各具特色。Webpack是一款全能型打包器,能处理多种静态资源,配置灵活,适合复杂项目;Rollup专注于ES6模块打包,利用Tree Shaking技术减少冗余,生成更精简的代码。Rollup构建速度快,配置简洁,而Webpack则拥有更丰富的插件生态系统。选择合适的工具需根据项目需求和个人偏好决定。两者都能有效提升前端工程化水平,助力高质量应用开发。
51 1
|
4月前
|
JavaScript 前端开发 API
解锁前端开发新境界:Vue.js携手Webpack,打造高效构建流程,你的项目值得拥有!
【8月更文挑战第30天】随着前端技术的发展,模块化与组件化趋势愈发显著。Vue.js 以其简洁的 API 和灵活的组件系统,深受开发者喜爱;Webpack 则凭借强大的模块打包能力成为前端工程化的基石。两者结合,不仅简化了组件编写与引用,还通过模块热替换、代码分割等功能大幅提升开发效率。本文将通过具体示例,展示如何利用 Vue.js 和 Webpack 构建高效、有序的前端开发环境。从安装配置到实际应用,逐步解析这一组合的优势所在。
53 0
|
4月前
|
JavaScript 测试技术
在不同 webpack 版本的 Vue 项目中配置 Storybook
在不同 webpack 版本的 Vue 项目中配置 Storybook
|
缓存 资源调度 JavaScript
【脚手架】从0到1搭建React18+TS4.x+Webpack5项目(一)项目初始化
【脚手架】从0到1搭建React18+TS4.x+Webpack5项目(一)项目初始化