最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Svelte 是如何用 RollUp 构建应用的?—— Svelte 模板解读

    正文概述 掘金(zhuzilin)   2021-03-19   687

    对 Svelte 框架有所耳闻的朋友可能都听说过:和 React 或 Vue 不同, Svelte 并不采用 Virtual DOM,而是在部署前将代码编译为原生的 DOM 操作和 vanilla js,从而在运行 Web 应用的时候不需要依赖框架本身的运行时,也不需要做 diff/patch 这样的操作,从而提升运行速度,并降低包的大小。(对 svelte 的简单解读可以看看尤大的知乎回答)

    根据上面的这个简介,一个很自然的问题就是,我们该怎么进行这个所谓的编译呢?官方并没有直接给出回答,而是推荐我们使用官网上的 REPL,一个根据输入代码实时渲染页面的在线编辑器(类似一个绑定了 Svelte 的 CodePen)。在 REPL 页面上制作出预想的效果后,下载为 svelte-app.zip,解压后运行:

    cd /path/to/svelte-app
    npm install
    npm run dev
    

    就能在 localhost:5000 访问到 REPL 的结果了。

    Svelte 是如何用 RollUp 构建应用的?—— Svelte 模板解读

    REPL 隐藏了 Svelte 的底层机制,让用户只用专注于写 .svelte 文件就好,的确很方便。不过为了知其然也要知其所以然,今天就让我们看看从 REPL 下载下来的 Svelte 模板项目是什么样的。

    Svelte 模板的文件夹结构

    直接下载 REPL 的初始应用,也就是上面图片中的 Hello World,得到的文件夹有这样的结构:

    svelte-app
    ├── .gitignore
    ├── README.md
    ├── package.json
    ├── rollup.config.js
    ├── public
    │   ├── favicon.png
    │   ├── global.css
    │   └── index.html
    ├── scripts
    |   └── setupTypeScript.js
    └── src
        ├── App.svelte
        └── main.js
    

    因为本文的重点不是 Svelte 的 TypeScript 用法,所以我们可以不考虑 scripts 文件夹,再删去几个和代码没什么关系的文件,模板就简化为了:

    svelte-app
    ├── package.json
    ├── rollup.config.js
    ├── public
    │   ├── favicon.png
    │   ├── global.css
    │   └── index.html
    └── src
        ├── App.svelte
        └── main.js
    

    熟悉 React 的朋友应该感觉这个结构很眼熟,和 create-react-app 生成的模板一样,有 srcpublic 两个子目录。

    src

    src 文件夹里面有 Svelte 的代码。其中 App.svelte 里面的内容和上面截图中的一致:

    <script>
    	let name = 'world';
    </script>
    
    <h1>Hello {name}!</h1>
    

    main.js 则是把 App 连接到最终页面上的桥梁:

    import App from './App.svelte';
    
    var app = new App({
    	target: document.body
    });
    
    export default app;
    

    这里的 target: document.body 就是把 App 渲染为 body 的子节点。

    public

    public 文件夹则是包含了最终的 HTML 页面。其中的 favicon.png 是一个 svelte 图标,global.css 是默认的全局 CSS,我们主要来看一下 index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset='utf-8'>
    	<meta name='viewport' content='width=device-width,initial-scale=1'>
    	<title>Svelte app</title>
    	<link rel='icon' type='image/png' href='/favicon.png'>
    	<link rel='stylesheet' href='/global.css'>
    
    	<link rel='stylesheet' href='/build/bundle.css'>
    	<script defer src='/build/bundle.js'></script>
    </head>
    
    <body>
    </body>
    </html>
    

    有两个值得关注的点。首先 index.html 中的 body 是空的,上文提到的 src/main.js 会用框架填充内容。其次则是下面的两行:

    	<link rel='stylesheet' href='/build/bundle.css'>
    	<script defer src='/build/bundle.js'></script>
    

    它们引用了目前还不存在的 bundle.cssbundle.js。这两个文件就是就是编译的结果了,可以猜测 Svelte 模板会把 src 中的代码编译进 public/build 文件夹中。

    RollUp 是如何构建 Svelte 应用的

    知道了编译的起始点(src) 和终点(public/build),那么编译器是如何完成这个转化的呢?根据启动项目的指令 npm run dev,让我们来看看对应的 package.json

    {
      "name": "svelte-app",
      "version": "1.0.0",
      "scripts": {
        "build": "rollup -c",
        "dev": "rollup -c -w",
        "start": "sirv public"
      },
      ...
    }
    

    npm run dev 相当于 rollup -c -w,也就是说 Svelte 利用的是 RollUp 进行编译和打包的。给不了解的朋友们介绍一下,RollUp 是一个 JavaScript 打包器,类似 Webpack,只是 RollUp 专注于打包 JavaScript。rollup -c -w 会让 RollUp 执行打包操作,其中 -c 指使用项目中的 rollup.config.js 作为配置文件,-w 则表示监听待打包的文件们,一旦有改动,就会自动重新打包。

    所以执行 Svelte 的编译,以及最后在本地托管静态网页的秘密都在 rollup.config.js 中了。我们先把整个文件列在这里(对默认的英文注释进行了翻译):

    import svelte from 'rollup-plugin-svelte';
    import commonjs from '@rollup/plugin-commonjs';
    import resolve from '@rollup/plugin-node-resolve';
    import livereload from 'rollup-plugin-livereload';
    import { terser } from 'rollup-plugin-terser';
    import css from 'rollup-plugin-css-only';
    
    const production = !process.env.ROLLUP_WATCH;
    
    function serve() {
      let server;
    
      function toExit() {
        if (server) server.kill(0);
      }
    
      return {
        writeBundle() {
          if (server) return;
          server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
            stdio: ['ignore', 'inherit', 'inherit'],
            shell: true
          });
    
          process.on('SIGTERM', toExit);
          process.on('exit', toExit);
        }
      };
    }
    
    export default {
      input: 'src/main.js',
      output: {
        sourcemap: true,
        format: 'iife',
        name: 'app',
        file: 'public/build/bundle.js'
      },
      plugins: [
        svelte({
          compilerOptions: {
            // 在非生产环境中开启运行时检查
            dev: !production
          }
        }),
        // 将所有组件中的 CSS 提取进一个文件——提高性能
        css({ output: 'bundle.css' }),
    
        // 如果你有从 npm 安装的外部依赖,那么你一般都需要这些插件。
        // 一些情况下你需要进行额外的配置——详情见文档:
        // https://github.com/rollup/plugins/tree/master/packages/commonjs
        resolve({
          browser: true,
          dedupe: ['svelte']
        }),
        commonjs(),
    
        // 在非生产环境中,在打包生成后运行 `npm run start`
        !production && serve(),
    
        // 在非生产环境中,在 `public` 目录中有改动时刷新浏览器
        !production && livereload('public'),
    
        // 如果为生产环境进行构建(npm run build 而不是 npm run dev),
        // minify
        production && terser()
      ],
      watch: {
        clearScreen: false
      }
    };
    

    rollup.config.js 通用结构

    让我们先挑出 RollUp 配置的通用结构,也就是:

    export default {
      input: 'src/main.js',  // 输入文件为 src/main.js
      output: {
        sourcemap: true,
        // 把生成的代码为 (function () { code })() 形式
        // 详见:https://en.wikipedia.org/wiki/Immediately_invoked_function_expression
        format: 'iife',
        name: 'app',
        // 输出至 public/build/bundle.js
        file: 'public/build/bundle.js'
      },
      plugins: [
        ...
      ],
      watch: {
        // 在重新打包的时候不要清空终端
        clearScreen: false
      }
    };
    

    这部分代码印证了上面我们的猜测,也就是 RollUp 会把 src 中的文件打包至 public/build/bundle.js。不过这里只提到了 main.js.svelte 文件该怎么办呢?这就要取决于 Svelte 模板使用的 RollUp 插件了。

    RollUp 插件

    export default {
      ...
      plugins: [
        svelte({
          compilerOptions: {
            // 在非生产环境中开启运行时检查
            dev: !production
          }
        }),
        // 将所有组件中的 CSS 提取进一个文件——提高性能
        css({ output: 'bundle.css' }),
    
        // 如果你有从 npm 安装的外部依赖,那么你一般都需要这些插件。
        // 一些情况下你需要进行额外的配置——详情见文档:
        // https://github.com/rollup/plugins/tree/master/packages/commonjs
        resolve({
          browser: true,
          dedupe: ['svelte']
        }),
        commonjs(),
    
        // 在非生产环境中,在打包生成后运行 `npm run start`
        !production && serve(),
    
        // 在非生产环境中,在 `public` 目录中有改动时刷新浏览器
        !production && livereload('public'),
    
        // 如果为生产环境进行构建(npm run build 而不是 npm run dev),
        // minify
        production && terser()
      ],
      ...
    };
    

    插件会根据定义的顺序依次执行。这里我们可以把插件分为 3 组。

    1. Svelte 编译插件

    import svelte from 'rollup-plugin-svelte';
    import css from 'rollup-plugin-css-only';
    
    export default {
      ...
      plugins: [
        svelte({
          compilerOptions: {
            // 在非生产环境中开启运行时检查
            dev: !production
          }
        }),
        // 将所有组件中的 CSS 提取进一个文件——提高性能
        css({ output: 'bundle.css' }),
         ...
       ],
       ...
    };
    

    rollup-plugin-svelte 会调用 Svelte 编译器编译,由于篇幅所限,我会在后续的文章中给大家详细拆解一下这个插件。目前大家只需要知道:

    • 在默认配置下,rollup-plugin-svelte 会编译所有的 .svelte 文件;
    • rollup-plugin-svelte 先后使用了 svelte 包中的 preprocesscompiler 函数。

    rollup-plugin-css-only 则是一个和 Svelte 不直接相关的插件,但是因为 Svelte 编译器会单独处理每个组件的 CSS 片段,所以需要使用这个插件把 CSS 合并起来。

    2. 通用插件

    import commonjs from '@rollup/plugin-commonjs';
    import resolve from '@rollup/plugin-node-resolve';
    
    export default {
      ...
      plugins: [
        ...
        // 如果你有从 npm 安装的外部依赖,那么你一般都需要这些插件。
        // 一些情况下你需要进行额外的配置——详情见文档:
        // https://github.com/rollup/plugins/tree/master/packages/commonjs
        resolve({
          browser: true,
          dedupe: ['svelte']
        }),
        commonjs(),
        ...
      ],
      ...
    };
    

    第二组是 RollUp 的 2 个通用插件,resolve 是为了把依赖项打进包里,commonjs 则是为了把 CommonJS 模块转化为 ES2015 供 RollUp 处理。对于这两个插件的用例介绍,请见 RollUp 文档。

    3. 调试环境与生产环境用插件

    import livereload from 'rollup-plugin-livereload';
    import { terser } from 'rollup-plugin-terser';
    
    const production = !process.env.ROLLUP_WATCH;
    
    export default {
      ...
      plugins: [
        ...
        // 在非生产环境中,在打包生成后运行 `npm run start`
        !production && serve(),
    
        // 在非生产环境中,在 `public` 目录中有改动时刷新浏览器
        !production && livereload('public'),
    
        // 如果为生产环境进行构建(npm run build 而不是 npm run dev),
        // minify
        production && terser()
      ],
      ...
    };
    

    在模板项目的配置文件中,通过查看是否启用了 RollUp 的监听功能(ROLLUP_WATCH)来判断当前是否处于生产环境。最后一组的 3 个插件则和是否处于生产环境息息相关。注释已经把 livereloadterser 的作用讲得很清楚了,就不再赘述。我们来看看 serve 这个插件。它是唯一一个在配置文件中编写的插件,也是负责托管静态网页的重要角色。它的源码如下:

    function serve() {
      let server;
    
      function toExit() {
        if (server) server.kill(0);
      }
    
      return {
        writeBundle() {
          if (server) return;
          server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
            stdio: ['ignore', 'inherit', 'inherit'],
            shell: true
          });
    
          process.on('SIGTERM', toExit);
          process.on('exit', toExit);
        }
      };
    }
    

    serve 使用了 RollUp 的一个钩子函数 writeBundle,这个函数会在打包完成的时候被调用。对于 serve 来说,就是在打包完成的时候启动(spawn)一个新进程,运行 npm run start。对应 package.json 的话就是运行:

    sirv public
    

    sirv 是一个用来托管静态文件的 cli 应用,Svelte 模板就是利用它来完成最后的预览工作的。

    到这里我们就介绍完了 Svelte 模板了~ 到这里你是不是对 Svelte REPL 生成的模板应用多了一份了解呢?在后续的文章中,我会再进一步,聊一聊 rollup-plugin-svelte 里面都做了什么,敬请期待~ 如果喜欢本文的话,也别忘了点个赞哦!


    起源地下载网 » Svelte 是如何用 RollUp 构建应用的?—— Svelte 模板解读

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元