飘刃 (Piao Ren)

约定大于配置的极速 Web 应用打包工具,支持 .vue 文件,生产使用 Rollup 打包

仅供学习研究,请勿用于生产

仓库

【Github】 https://github.com/chjtx/pr1

【 码 云 】 https://gitee.com/chenjianlong/pr1

特点优势

飘刃 VS Vue-CLI

对比环境 华为荣耀 MagicBook Windows 10 家庭版 i5 8G 64位 联通4G热点 30多个组件的小型 Vue 项目

飘刃 Vue-CLI
工具版本 piaoren@0.1.1 @vue/cli@3.6.3
依赖包数 487 689
安装命令 npm i -g piaoren npm i -g @vue/cli
安装时间 38s 1m 42s
支持编码 Pug Sass ES6+ Pug Sass Less Stylus ES6+ TypeScript
创建项目 pr1 init 只需要填项目名称 vue create/vue init 需要填选多项
启动命令 pr1 start vue serve
启动时间 2s 与项目内容多少无关 6.8s 项目内容多少决定
热更响应 支持更新 css 和刷新页面
两种方式,不支持 js 更新
更新 js 需要刷新页面
响应速度 立即
支持 css 和 js 更新,vue 组件更新
有点鸡肋,很大概率需要手动更新
才能看到预期效果,每次变化都需
要编译,响应速度 稍慢
打包工具 Rollup Webpack
打包时间 5s 项目内容多少决定 10s 项目内容多少决定
静态资源 暂只支持./开头
的静态资源
支持./@~
开头的静态资源
多页应用 无需配置 需要配置 pages
插件支持 Rollup 插件规范 Webpack 插件规范
单元测试 暂不支持 可选

总结:飘刃安装时间、启动速度、响应速度、打包时间都优于 Vue-CLI,但是配置方面不及 Vue-CLI 丰富。中小型无需配置的项目选择飘刃,大中型需要多方面资源配合的项目选择 Vue-CLI。

快速上手

npm i -g piaoren

把飘刃安装到全局,任意目录都可以运行飘刃的命令 pr1

pr1 init

? Project name:           # 项目名称至少两个字符,由大小写字母、中划线、下划线,及数字组成,数字不能为首字符
? Project description:    # 可不填

将会自动生成项目名称命名的文件夹,包含若干工程文件

进入工程目录,执行以下命令开启开发模式

npm run dev
// main.js
import Vue from 'vue/dist/vue.esm.browser.js'
import Layout from './pages/Layout.vue'

// eslint-disable-next-line no-new
new Vue({
  el: '#app',
  components: {
    Layout
  },
  template: '<Layout/>'
})
<!-- pages/Layout.vue -->
<template lang="pug">
div
  div.top
    input(v-model="text")
    button(@click="submit") 添加
  ul
    Item(v-for="(i, k) in items" :name="i" :key="k")
</template>
<script>
import Item from './Item.js'
export default {
  components: {
    Item
  },
  data () {
    return {
      text: '',
      items: []
    }
  },
  methods: {
    submit () {
      this.items.push(this.text)
      this.text = ''
    }
  }
}
</script>
<style lang="sass" scoped>
$bg: #ccc;

.top {
  padding: 20px;
  background: $bg;
}
</style>
// pages/Item.js
import html from './Item.html'

export default {
  template: html,
  props: {
    name: String
  }
}
<!-- pages/Item.html -->
<li class="item">{{ name }}</li>

<style scoped>
.item {
  background: #eee;
}
</style>

在浏览器访问 http://localhost:8686/

以上例子演示了两种写 Vue 组件的方法

一、使用 .vue 文件,目前 .vue 文件只支持普通的 html/css/js 和 sass/pug ,不支持 less/typescript 等。注意 Layout.vue 的 pug ,首行第一个位置不能是空格,即不能缩进。

二、使用 html 和 js 两个文件写 Vue 组件,如果存在同路径同名的两个文件,例:Component.html 和 Component.js,则飘刃会把这两个文件处理成 Vue 组件。需要注意的是,这种方式的 html 文件不包括 script ,所以不需要 template 标签,直接写 div ,也不支持 pug。

开发完成后,使用以下命令打包

npm run build

打包完成后可在 dist 目录双击 index.html 到浏览器访问,如果项目包含 ajax 请求,file:// 协议文件无法跨域,可以在 dist 目录运行 pr1 start 8080 开启飘刃服务,在浏览器访问 http://localhost:8080/

命令说明

# 创建项目,初始化工程文件

pr1 init
# 开启飘刃服务,在哪个目录开启,哪个目录就是根站点
# 可在浏览器访问该站点文件,相当于微型静态服务器
# 将会拦截所有带 pr1_module=1 参数的 url 进行文件处理
# 用于开发环境

pr1 start [port] [config]

# 示例

pr1 start     # 默认 8686 端口,工程根目录的 pr1.config.js 配置文件
pr1 start 8080  # 指定 8080 端口
pr1 start --config="./config.js" # 指定 ./config.js 配置文件
pr1 start 8080 --config="./config.js" # 指定端口和配置文件
# 使用 build 命令打包,必须指定入口文件,只能是 html 和 js 文件
# 可以同时打包多个模块
# 如果是 html 文件,将会自动解决 html 里的入口文件及复制静态资源
# 如果是 js 文件,只会解决该 js 文件及其依赖,不会复制静态资源
# 入口文件目录与打包后的文件目录结构相同

pr1 build [entry] [config]

# 示例

pr1 build index.html # 使用默认配置文件打包
pr1 build index.html --config="./config.js" # 使用指定配置文件
pr1 build index.html detail.html tools.js   # 同时打包3个文件
pr1 build index.html detail.html tools.js --config="./config.js" # 使用指定配置打包3个文件
pr1 build page1/index.html page2/index.html # 在打包后也会保持同样结构

配置说明

// pr1.config.js
const nodeResolve = require('rollup-plugin-node-resolve')
require('colors')

module.exports = {
  // vendor 子项的第一个值将会整合到 rollup 的 external
  // [0]是开发环境用的,[1]是生产环境用的,如果没有[1]生产环境也用[0]
  vendor: [
    ['vue/dist/vue.esm.browser.js', 'vue/dist/vue.runtime.min.js']
  ],
  // true 表示存在同级目录且同名的 html 和 js 文件会被关联到一起
  // 转成 Vue render 组件提高性能,仅生产环境起作用
  html2VueRender: true,
  // 热更新 true or reload,如果是字符串 reload,将会刷新浏览器而非 .vue 组件
  hot: true,
  // dist 打包后文件输出目录,路径应相对于当前配置文件
  dist: '',
  // static 静态文件,路径应相对于 html 入口文件
  static: [],
  // rollup 选项,必须有
  rollupConfig: {
    // 定义了 vendor,再定义 globals,这样 rollup 会把相关的 vendor 转换成全局变量作为外部资源处理
    globals: {
      'vue/dist/vue.esm.browser.js': 'Vue'
    },
    plugins: [
      // rollup-plugin-node-resolve 用于解决引用 node_modules 资源路径
      nodeResolve()
    ]
  },
  // babel 选项,不提供将不会进行转码,不使用 uglify 压缩时,可安装 babel 的 minify 插件压缩
  babelConfig: {
    presets: [
      [
        '@babel/env', {
          modules: false,
          targets: {
            ie: '9',
            chrome: '49'
          }
        }
      ]
    ]
  },
  // uglify 选项,不提供将不会压缩,如果 babel 转码后仍存在 ES6+ 代码,uglify 解释不了将会压缩失败
  uglifyConfig: {
    toplevel: true
  },
  // 打包前的钩子
  beforeBuild: async function (originDir) {
    console.log(`开始打包:`.green + `${originDir}`.cyan)
  },
  // 打包后的钩子
  afterBuild: async function (distDir) {
    console.log(`完成打包:`.green + `${distDir}`.cyan)
  }
}

运作原理

飘刃会拦截所有带有 pr1_module=1 参数的 url ,并处理对应的文件资源,目前只会处理 .vue .html .js 3种文件

把 import/export 转换成 async/await 让浏览器可以支持引入除 js 外的其它资源

飘刃会把非 js 资源通过 rollup 的插件转换成 js 资源再传到浏览器,开发环境只会调用 rollup 插件的 resolveIdtransform 方法

import/export 转换关系如下:

// import 规则
import { a, b, c } from './util.js'             => const { a, b, c } = await _import('./util.js')
import { abc as a, efg as b } from './util.js'  => const { abc: a, efg: b } = await _import('./util.js')
import a from './util.js'                       => const { default: a } = await _import('./util.js')
import './util.js'                              => await _import('./util.js')
import * as a from './util.js'                  => const a = await _import('./util.js')
import a, { efg as b, c } from './util.js'      => const { default: a, efg: b, c } = await _import('./util.js')
// export 规则
 export var a = 'xxx'                           => var a = exports.a = 'xxx'
 export { a, b, c }                             => Object.assign(exports, {a, b, c})
 export function a () {}                        => exports.a = a; function a () {}
 export default a                               => exports.default = a
 export { abc as a }                            => Object.assign(exports, {a: abc} = { a })
 export class e {}                              => exports.e = e; class e {}
 export { default as d } from './util.js'       => Object.assign(exports, await (async () => { const { default: d
                                                   } = await _import('./util.js'); return { d }})())

静态资源

支持两种静态资源路径 以 / 开头和以 . 开头

如下目录结构

src/
  |-- index.html
  |-- static/
    |-- images/
      |-- a.png
  |-- pages/
    |-- one/
      |-- two/
        |-- three/
          |-- a.vue # background-url: /static/images/a.png
    |-- b.html  # img src="../static/images/a.png"
    |-- b.js

绝对路径和相对路径都是引用同一张图片

支持组件多 style ,组件内 style 均为组件内样式,带 scoped 不会影响子组件,不带 scoped 会影响子组件,全局样式请用 link 标签引入

多页应用

飘刃处理多面应用非常简单,如下目录结构

src/
  |-- page1/
    |-- index.html
  |-- page2/
    |-- index.html
  |-- page3/
    |-- index.html

开发环境,在 src 目录执行 pr1 start

在浏览器分别访问

http://localhost:8686/page1/

http://localhost:8686/page2/

http://localhost:8686/page3/

生产环境,在 src 目录执行 pr1 build page1/index.html page2/index.html page3/index.html

在 dist 目录会保持以下目录结构

dist/
  |-- page1/
    |-- index.html
  |-- page2/
    |-- index.html
  |-- page3/
    |-- index.html

支持 Typescript

目前只支持引入 .ts 文件,.vue 组件的 type=ts 暂不支持

# 安装
npm i rollup-plugin-typescript typescript tslib

修改 pr1.config.js 添加 typescript 插件

const typescript = require('rollup-plugin-typescript')
// ...

module.exports = {
  rollupConfig: {
    // ...
    plugins: [
      typescript({
        target: 'ESNext',
      })
      // ...
    ]
  },
  // ...
}

生产注释

如下示例:

doSome(data => {
  // pr1 ignore++
  console.info(`调试信息:${data}`)
  // pr1 ignore--
  doIt(data)
})

// pr1 ignore++// pr1 ignore--的代码在生产环境会被删除

doSome(data => {
  doIt(data)
})

注意事项

<template>
<div class="top">
  <div class="abc">123</div>
</div>
</template>
<style>
/* 不带 scoped 必须使用 this 表示最外层 dom 选择器 */
this {
  background: #efefef;
}
.abc {
  font-size: 20px;
}
</style>
<style scoped>
/* 带 scoped 可以使用最外层 dom 的 class 来做选择器 */
.top {
  font-size: 22px;
}
</style>

常见错误

更新日志

### v0.3.16 (2020-04-11)

### (2019-12-30)

### v0.3.13 (2019-12-07)

### v0.3.12 (2019-10-29)

### v0.3.11 (2019-10-04)

### v0.3.10 (2019-08-12)

### v0.3.9 (2019-06-28)

### (2019-06-02)

### v0.3.5 (2019-05-26)

### (2019-05-25)

### (2019-05-24)

### v0.3.0 (2019-05-23)

### (2019-05-22)

### v0.2.10 (2019-05-12)

### v0.2.9 (2019-05-08)

### (2019-05-06)

### v0.2.6 (2019-05-04)

### (2019-05-02)

### v0.2.1 (2019-04-30)

### v0.2.0 (2019-04-29)

### v0.1.1 (2019-04-26)

### v0.0.10 (2019-04-24)

攒助作者

支持作者继续维护更新,编写更多教程和使用技巧。如果有足够的支持,飘刃将来将会支持 React、TypeScript、异步模块等等。

支持方式

打赏1块几毛钱,让作者不用去天桥底蹲位

开源协议

MIT