Scaffolding tools (e.g. Vue-cli, Vite) allow us to do Vue development more efficiently, and learning how they work can help improve development skills. Practice is the most out of the truth, I started from scratch hand a scaffolding tool, to deepen the understanding of the principle of scaffolding. No more nonsense, first on the code address.

Sps-vue-cli (gitee.com)

Initialize the

First, implement the core functionality of the scaffolding tool —— code packaging.

Create the project folder, generate the package.json configuration file in the folder, and install the rollup package.

npm init
yarn add rollup -D
Copy the code

Create the entry file.

// src/main.js
console.log('hello vue')
Copy the code

Configure entry file path and output file and format for rollup.

// rollup.config.js
export default {
  input: 'src/main.js'.output: {
    file: 'bundle.js'.format: 'cjs'}}Copy the code

Configure the dev directive. The c parameter means to execute the compilation, and the w parameter means to listen to the file status and automatically recompile the file when it is modified

// package.json
"scripts": {
  "dev": "rollup -wc"
 }
Copy the code

After the yarn dev command is executed, you can see that a bundle.js file is added to the root folder. The bundle.js file contains the packaged content of main.js

TS plug-in

TS has become the official standard of Vue3. Using TS can improve the standardization of code and reduce bugs caused by types, especially in large projects.

Transform the entry file into a TS file.

// src/main.ts
interface Test {
  name: string
}

const s: Test = { name: 'sps' }
document.write(s.name)
Copy the code

Add tsconfig.json to the root directory and do the basic configuration.

// tsconfig.json
{
  "compilerOptions": {
    "module": "esnext".// Syntax version
    "strict": false.// Strict mode
    "baseUrl": ".".// Base directory
    "paths": {
      / / alias configuration
      "@ / *": ["src/*"]}},// ts range of files processed by the compiler
  "include": ["src/**/*.ts"."src/**/*.d.ts"."src/**/*.tsx"."src/**/*.vue"]}Copy the code

Install and introduce the @rollup/plugin-typescript plugin, in addition to installing the ts compiler package tslib typescript.

// rollup.config.js
import ts from '@rollup/plugin-typescript'

export default {
  input: 'src/main.ts'.output: {
    file: 'bundle.js'.format: 'cjs'
  },
  plugins: [
    ts({
      tsconfig: './tsconfig.json'}})]Copy the code

Open the bundle.js file again. The ts file has been compiled to js. As an added bonus, the const keyword used in ts files is incidentally changed to var. This is because ts is a superset of ES, supports the latest syntax of ES, and controls the compiled version of js files by configuring the target attribute (default is ES3).

HTML

Create an HTML file in which to introduce the bundle.js file.

<! doctype html><html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Sps-Vue-Cli</title>
    
  </head>
  <body>
    <div id="app"></div>
    <script src="bundle.js"></script>
  </body>
</html>
Copy the code

Now you can open the HTML file in your browser

Server plug-in and hot update plug-in

Currently rollup automatically recompiles after changes are made to the code, but you must manually refresh the browser to see the changed page.

Install and introduce rollup-plugin-serve rollup-plugin-livereload to implement hot updates to the code.

// rollup.config.js
/ /...
import server from 'rollup-plugin-serve'
import liverload from 'rollup-plugin-livereload'

export default {
  / /...
  plugins: [
    / /...
    server({
      open: true.openPage: '/index.html'.port: 5000
    }),
    liverload()
  ]
}
Copy the code

Vue

The next step is to introduce Vue in the scaffolding.

Simple to use

Create a Vue component with the render function.

// src/App.ts
import { defineComponent, h } from 'vue'

export default defineComponent({
  name: 'App',
  setup () {
    return () = > {
      return h('div'.null.'sps')}}})Copy the code

Mount the component to the DOM node in main.ts.

// src/main.ts
import { createApp } from 'vue'
import App from './App'

const app = createApp(App)
app.mount('#app')
Copy the code

Since the import syntax is used here for the module reference, you need to configure it in tsconfig.json.

// tsconfig.json
"moduleResolution": "node".Copy the code

There are three issues that need to be addressed:

  1. Rollup itself cannot handle thisvueThe external module must be installed and imported@rollup/plugin-node-resolveThe plug-in.
  2. vueSource code in many places to use environment variables, executionprocess.env.NODE_ENVOperation will be reported error and promptprocessforundefined, install and introduce@rollup/plugin-replaceThe plug-in can compile the code when theprocess.env.NODE_ENVFor example, replace the environment variable with a specific value.
  3. The environment variable__VUE_OPTIONS_API__, __VUE_PROD_DEVTOOLS__If the initialization is not performed, a warning will be reported, so that you can configure it for yourself.
// rollup.config.js
/ /...
import { nodeResolve } from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'

const env = process.env.NODE_ENV

export default {
  / /...
  plugins: [
    nodeResolve(),
    ts({
      tsconfig: './tsconfig.json'
    }),
    replace({
      preventAssignment: true.// Prevent environment variables from being modified in the code
      'process.env.NODE_ENV': JSON.stringify(env),
      '__VUE_OPTIONS_API__': true.'__VUE_PROD_DEVTOOLS__': true
    }),
    / /...]}Copy the code

In a real development scenario, it is common not to use the Render function directly for development, but to use JSX or single-file components. By default, ROLLup is unable to compile files in formats other than.js, so additional processing is required.

jsx

First configure support for JSX syntax in tsconfig.json.

// tsconfig.json
"jsx": "preserve"
Copy the code

Install and introduce the @rollup/plugin-babel plugin (in this order, Babel must come before TS, otherwise TS will fail to compile JSX syntax).

// rollup.config.js
/ /...
import babel from '@rollup/plugin-babel'

const env = process.env.NODE_ENV
const extensions = ['.js'.'.ts'.'tsx']

export default {
  / /...
  plugins: [
    / /...
    babel({
      exclude: 'node_modules/**'.babelHelpers: 'bundled',
      extensions
    }),
    ts({
      tsconfig: './tsconfig.json'
    }),
    / /...]}Copy the code

Install the packages related to Babel: @babel/ core@babel /preset-env @babel/preset- typescript@vue /babel-plugin-jsx. Add a. Babelrc file to the root directory for configuration.

// .babelrc
{
  "presets": [
    "@babel/env"."@babel/preset-typescript"]."plugins": [
    "@vue/babel-plugin-jsx"]}Copy the code

Then transform the App component into. TSX format for testing.

// src/App.tsx
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'App',
  setup () {
    return () = > {
      return (
        <div class="test">sps</div>)}}})Copy the code

SFC

Developing with a single file component requires the rollup-plugin-vue plug-in to be installed and introduced before the TS plug-in for the same reasons.

// rollup.config.js
/ /...
import vue from 'rollup-plugin-vue'

const env = process.env.NODE_ENV
const extensions = ['.js'.'.ts'.'tsx']

export default {
  / /...
  plugins: [
    / /...
    vue(),
    ts({
      tsconfig: './tsconfig.json'
    }),
    / /...]}Copy the code

In addition, you need to install the @vue/compiler-sfc package. There is a big pit here, the author toss about a little half a genius to find the reason. After the bag is installed directly @ vue/compiler – SFC rollup complains after compiling, some open source code to find an error site for @ vue/compiler – a compileScript SFC package function, in the second parameter of this function deconstruction times undefined error. A review of the rollup-plugin-vue source code shows that only one parameter was passed when the function was called, resulting in an error. Both packages are written by Vue, but rollup-plugin-vue has not been updated for several months, so it is likely that vite has been updated, while @vue/compiler-sfc has been updated. The version is consistent with the latest version of VUE, which is already the version of VUE 3.1.x, so you need to specify the version of @vue/compiler-sfc as 3.0.x for the installation.

You can then use single-file components in.vue format to develop.

// src/App.vue
<template>
  <div class="test">sps</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'App'
})
</script>
Copy the code

Remember to add the shims-vue-D. ts file in the SRC directory to declare the export type of the. Vue file, otherwise ts will report an error.

// src/shims-vue-d.ts
declare module '*.vue' {
  import { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}
Copy the code

Postcss

Install and introduce the rollup-plugin-PostCSS plug-in.

// rollup.config.js
/ /...
import postcss from 'rollup-plugin-postcss'

const env = process.env.NODE_ENV
const extensions = ['.js'.'.ts'.'tsx']

export default {
  / /...
  plugins: [
    / /...
    postcss({})
    / /...]}Copy the code

Next, install the PostCSS and the CSS preprocessor package you need.

CSS styles can then be written and introduced in main.ts.

// src/style/index.scss
.test {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  color: red;
  font-size: 60px;
}
Copy the code
// src/main.ts
import './style/index.scss'
Copy the code

Eslint

Install and introduce the rollup-plugin-esLint plug-in.

// rollup.config.js
/ /...
import { eslint } from 'rollup-plugin-eslint'

const env = process.env.NODE_ENV
const extensions = ['.js'.'.ts'.'tsx']

export default {
  / /...
  plugins: [
    / /...
    eslint({
      include: ['src/**.ts'.'src/**.tsx'].throwOnError: true
    }),
    / /...
}
Copy the code

Configure esLint rules that need to be used. For example, if no-console is configured as WARN, a compile-time rollup will show a warning if console.log is present in code.

// .eslintrc.js
module.exports = {
  env: {
    browser: true
  },
  parserOptions: {
    ecmaVersion: 2020.sourceType: 'module'
  },
  rules: {
    'no-console': 'warn'}}Copy the code

Configures folders and files to be ignored when performing ESLint rule checks.

// .eslintignore
/node_nodules/*
/dist/*
rollup.config.js
bundle.js
Copy the code

Build

Above the realization of the development mode of scaffolding function, in the real environment, also need to realize the function of product mode.

First, the common configuration items of the two modes are extracted.

// rollupConfig/index.js
import ts from '@rollup/plugin-typescript'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import babel from '@rollup/plugin-babel'
import vue from 'rollup-plugin-vue'
import postcss from 'rollup-plugin-postcss'
import { eslint } from 'rollup-plugin-eslint'

const env = process.env.NODE_ENV
const extensions = ['.js'.'.ts'.'tsx']

export default {
  input: 'src/main.ts'
}

export const plugins = [
  nodeResolve(),
  babel({
    exclude: 'node_modules/**'.babelHelpers: 'bundled',
    extensions
  }),
  vue(),
  eslint({
    include: ['src/**.ts'.'src/**.tsx'].throwOnError: true
  }),
  ts({
    tsconfig: './tsconfig.json'
  }),
  postcss({}),
  replace({
    preventAssignment: true.'process.env.NODE_ENV': JSON.stringify(env),
    '__VUE_OPTIONS_API__': true.'__VUE_PROD_DEVTOOLS__': true})]Copy the code

Server and hot update plug-ins are only needed in development mode.

// rollup.config.dev.js
import rollupConfig, { plugins } from './rollupConfig/index'
import server from 'rollup-plugin-serve'
import liverload from 'rollup-plugin-livereload'

export default {
  ...rollupConfig,
  output: {
    file: 'bundle.js'.format: 'cjs'
  },
  plugins: [
    ...plugins,
    server({
      openPage: '/index.html'.port: 5000
    }),
    liverload()
  ]
}
Copy the code

In production mode, install and introduce the rollup-plugin-terser plug-in to minimize the compiled code.

// rollup.config.prod.js
import rollupConfig, { plugins } from './rollupConfig/index'
import { terser } from 'rollup-plugin-terser'

export default {
  ...rollupConfig,
  output: {
    file: 'dist/index.js'.format: 'cjs'
  },
  plugins: [
    ...plugins,
    terser()
  ]
}

Copy the code

In package.json, configure instructions for the two modes. In production mode, you don’t need to monitor code changes, so you only need the -c parameter.

// package.json
"scripts": {
  "dev": "rollup -wc rollup.config.dev.js"."build": "rollup -c rollup.config.prod.js"
}
Copy the code

conclusion

After the above operations, the most basic functions of Vue scaffolding tool have been realized. However, compared with such mature tools as VUe-CLI and Vite, further improvement is needed, such as the reverse proxy function of the development server, automatic block partitioning function of JS files after compilation, and so on. The next step is to expand it as needed.