美文网首页
Loader 原理

Loader 原理

作者: sweetBoy_9126 | 来源:发表于2021-09-28 16:23 被阅读0次

    针对上一篇文章我们的 bundler 只能加载 js,那么我们如何让它支持 css 那?

    思路:
    1). 我们的 bundler 只能加载 js
    2). 我们想要加载 css
    如果我们能把 css 变成 js, 那么就可以加载 css 了

    1. 把 css 变成 js

    复制 bundler_1.ts 为 bundler_css.ts

    let code = readFileSync(filepath).toString()
    if (/\.css$/.test(filepath)) {
        code = `
          const str = ${JSON.stringify(code)}
          export default str
        `
      }
    

    问题:上面的代码把 body {color: red} 变成
    const str = "body {color: red}"; export default str
    有什么 X 用?body 里的文字并不会变红啊

    1.1 改进

    if (/\.css$/.test(filepath)) { // 如果文件路径以 .css 结尾
        code = `
          const str = ${JSON.stringify(code)}
    +      if (document) {
    +        const style = document.createElement('style')
    +        style.innerHTML = str
    +        document.head.appendChild(style)
    +      }
          export default str
        `
      }
    

    测试复制 project_1 为 project_css
    新建一个style.css 文件,然后在index.js 里引入

    • style.css
    body {
        color: red;
    }
    

    运行 node -r ts-node/register bundler_css.ts
    然后直接在 dist 目录下创建 index.html 引入打包生成的文件

    解决过程

    1. project_1 目录复制为 project_css
    2. bungler_1.ts 复制为 bundler_css.ts
    3. 改一下 bundler_css.ts
    4. 改一下 project_css 让 index.js 加载一个 css 文件
    5. 创建 index.html 测试 css 是否生效

    2. 创建 css-loader

    将上面的 code 代码抽离为单独的 loader

    1. 新建一个 loaders/css-loader.js 文件
    const transform = (code) => `
      const str = ${JSON.stringify(code)}
      if (document) {
        const style = document.createElement('style')
        style.innerHTML = str
        document.head.appendChild(style)
      }
      export default str
    `
    module.exports = transform
    
    1. 复制一个 bundler_css.ts 并命名为 bundler_css_loader.ts
      将代码改为
    if (/\.css$/.test(filepath)) {
        code = require('./loaders/css-loader.js')(code)
      }
    

    2.1. 什么是 loader

    1). loader 可以是一个普通函数

    function transform(code) {
      const code2 = doSomething(code)
      return code2
    }
    module.exports = transform
    

    2). loader 也可以是一个异步函数

    async function transform(code) {
      const code2 = await doSomething(code)
      return code2
    }
    module.exports = transform
    

    核心代码


    为什么用 require 不用 import

    • 主要是为了方便动态加载

    3. 优化 loader

    单一职责原则
    webpack 里每个 loader 只做一件事
    而我们的 css-loader 做了两件事:
    1). 把 css 变为 js 字符串
    2). 把 js 字符串放到 style 标签里

    将我们的 loader 拆成 2 个
    新建一个 style-loader

    • loaders_2/style-loader.js
    const transform = (code) => `
      if (document) {
        const style = document.createElement('style')
        style.innerHTML = ${JSON.stringify(code)}
        document.head.appendChild(style)
      }
      export default str
    `
    module.exports = transform
    
    • loaders_2/css-loader.js
    const transform = (code) => `
      const str = ${JSON.stringify(code)}
      export default str
    `
    module.exports = transform
    
    • bundler_css_loader2.ts
    if (/\.css$/.test(filepath)) {
        code = require('./loaders_2/css-loader.js')(code)
        code = require('./loaders_2/style-loader.js')(code)
      }
    

    运行 node -r ts-node/register bundler_css_loader2.ts
    打包编译的代码如下

    style.innerHTML 里多了很多我们不需要的代码,实际上我们只想要 style.innerHTML = "{color: red}"

    3.1. 问题分析

    • style-loader 不是转译
      sass-loader、less-loader 这些 loader 是把代码从一种语言转译为另一种
      因此将这样的 loader 连接起来不会出问题
      但 style-loader 是在插入代码,不是转译,所以需要寻找插入时机和插入位置
      插入代码的时机应该是在获取到 css-loader 的结果之后
      插入代码的位置应该是在就代码的下面
    • Webpack 官方 style-loader 的思路
      style-loader 在 pitch 钩子里通过 css-loader 来 require 文件内容
      然后在文件内容后面添加 injectStylesIntoStyleTag(content, ...) 代码

    相关文章

      网友评论

          本文标题:Loader 原理

          本文链接:https://www.haomeiwen.com/subject/vwjrultx.html