从 0 到 1 创建一个自己的组件

1,187 阅读5分钟

从 0 到 1 创建一个自己的组件

什么是Rollup

Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。Rollup 对代码模块使用新的标准化格式,这些标准都包含在 JavaScript 的 ES6 版本中,而不是以前的特殊解决方案,如 CommonJS 和 AMD。ES6 模块可以使你自由、无缝地使用你最喜爱的 library 中那些最有用独立函数,而你的项目不必携带其他未使用的代码。ES6 模块最终还是要由浏览器原生实现,但当前 Rollup 可以使你提前体验。

rollup安装与使用

安装

    yarn add rollup -g  // 全局安装
    yarn add rollup -D  // 项目本地安装

创建一个项目

  • 新建文件夹,初始化文件夹
    yarn init -y
  • 安装 rollup
    yarn add rollup -D 
  • 新建一个文件夹src,src下创建两个js文件:index.js,hello.js
    • index.js
        import hello from "./hello";
        hello()
    
    • hello.js
        export default function hello(){
           console.log('hello');
        }
    
  • 运行打包
    • 如果是全局安装
      rollup -i src/index.js -o dist/bundle.js -f es
    
    • 如果是本地安装,package.jsonscript字段中添加: 然后执行yarn dev
          "dev": "rollup -i src/index.js -o dist/bundle.js -f es"
    
  • 得到的运行结果

image.png

image.png

  • rollup -i src/index.js -o dist/bundle.js -f es这段指令中
    • -i => 打包的文件 -i--input的缩写
    • src/index.js => 是指入口文件
    • -o => 输出的文件,-o--output.file 或者 --file 的缩写(没有这个参数,默认输出到控制台)
    • dist/bundle.js => 输出文件
    • -f => 打包文件的格式,-f--format的缩写。
    • es-f的参数,表示打包文件使用ES6模块规范。 rollup支持的打包文件的格式有amd, cjs, es\esm, iife, umd。其中,amdAMD标准,cjsCommonJS标准,esm\esES模块标准,iife为立即调用函数, umd同时支持amd、cjsiife

rollup 配置

webpack一样,rollup一样有配置文件去简化打包命令,同时还能更加科学的使用rollup

  • 在根目录下创建rollup.config.js
    export default {
        input: "./src/index.js",
        output: [{
                file: './dist/my-temp-umd.js',
                format: 'umd',
                name: 'myTemp'
                //当入口文件有export时,'umd'格式必须指定name
                //这样,在通过<script>标签引入时,才能通过name访问到export的内容。
            },
            {
                file: './dist/my-temp-es.js',
                format: 'es'
            },
            {
                file: './dist/my-temp-cjs.js',
                format: 'cjs'
            }
        ]
    }
  • 使用 rollup 配置文件可以使用rollup --config 或者 rollup -c
    //修改package.json的script字段
    "dev": "rollup -c"                 // 默认使用rollup.config.js
    "dev": "rollup -c rollup.dev.config.js"    //使用自定义的配置文件,rollup.dev.config.js,可以配置两套不同的打包配置,用以区分开发和生产环境
  • src/index.js
    import hello from './hello'

    hello()

    export const world = 'world'
  • 打包后的文件

image.png

rollup插件及其使用

以上仅仅是rollup的基本使用,在实际的使用环境下我们需要像webpack一样,对代码、依赖以及文件进行一系列处理,以及代码性能优化。

rollup-plugin-babel

rollup-plugin-babel用于转换es6语法。

src/hello.js中的内容改写成:

    export const hello = () => {
      console.log('hello world')
    }

箭头函数未转换

image.png

使用插件,安装yarn add rollup-plugin-babel @babel/core @babel/preset-env -D

//rollup.config.js
import babel from 'rollup-plugin-babel'
export default {
  input: ...,
  output: ...,
  plugins:[
    babel({
        exclude: 'node_modules/**'
    })
  ]
}

在根目录创建.babelrc文件

    {
      "presets": [
          [
            "@babel/preset-env"
          ]
        ]
    }

重新打包

image.png

@rollup/plugin-commonjs

rollup默认是不支持CommonJS模块的,自己写的时候可以尽量避免使用CommonJS模块的语法,但有些外部库的是cjs或者umd(由webpack打包的),所以使用这些外部库就需要支持CommonJS模块。

这里模拟下这个场景

新建文件src/util.js

    module.exports = { a: 1 }

src/index.js 中 引入

    import util from './util'
    console.log(util.a)

此时yarn dev 会报错

image.png

安装插件

    yarn add @rollup/plugin-commonjs -D
    //rollup.config.js
    import commonjs from '@rollup/plugin-commonjs'
    export default {
      input: ...,
      output: ...,
      plugins:[
        commonjs()
      ]
    }

重新打包

image.png

我们还可以使用require引入模块

    //src/index.js
    const util = require('./util')
    console.log(util.a)

rollup-plugin-postcss

处理css需要用到的插件是rollup-plugin-postcss。它支持css文件的加载、css加前缀、css压缩、对scss/less的支持

安装

    yarn add rollup-plugin-postcss -D
    //rollup.config.js
    import postcss from 'rollup-plugin-postcss'
    export default {
      input: ...,
      output: ...,
      plugins:[
        postcss()
      ]
    }

创建一个 css 文件在 src/index.js 引入

    //src/index.js
    import './css/index.css'
    //css/index.css
    body {
        font-size: 30px;
    }

重新打包

image.png

导入的css文件将用于生成style标签,插入到head中。

在根目录下我们新建一个src/index.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>
        <script src="./dist/my-temp-umd.js"></script>
        <div id="app">
            嘿嘿
        </div>
    </body>

    </html>

可以在网页结构中看到

image.png

该插件不能直接解析less、scss文件,在vue文件中的less、scss是可以解析的,解析vue文件的插件下文有介绍

autoprefixer

css3的一些属性加前缀,增加浏览器对css3的兼容

  • 安装
    yarn add autoprefixer@8.0.0 -D
    //rollup.config.js
    import postcss from 'rollup-plugin-postcss'
    import autoprefixer from 'autoprefixer'
    export default {
      input: ...,
      output: ...,
      plugins:[
        postcss({
          plugins: [  
            autoprefixer()  
          ]
        })
      ]
    }
  • 配置 browserslist

    1. 建立.browserslistrc文件
    2. 在package.json里面配置,在package.json中,添加"browserslist"字段。
    "browserslist": [
      "defaults",
      "not ie < 8",
      "last 2 versions",
      "> 1%",
      "iOS 7",
      "last 3 iOS versions"
    ]
    
  • 修改css内容

//src/css/index.css
body {
    font-size: 30px;
    display: flex;
}
  • 重新打包

image.png

cssnano

除了提高对css3的兼容,我们还要对css代码进行压缩那么cssnano就是用于压缩的插件

  • 安装
    yarn add cssnano -D
//rollup.config.js
import cssnano from 'cssnano'
export default {
  input: ...,
  output: ...,
  plugins:[
    postcss({
      plugins: [  
        cssnano()  
      ]
    })
  ]
}
  • 重新打包后 [ 去除了空格和换行 ]

image.png

抽离单独的css文件

rollup-plugin-postcss可配置是否将css单独分离,默认没有extract,css样式生成style标签内联到head中,配置了extract,就会将css抽离成单独的文件。

//rollup.config.js 
import postcss from 'rollup-plugin-postcss' 
export default {
    input: ...,
    output: ..., 
    plugins:[ 
        postcss({
            plugins: [  
                autoprefixer(),
                cssnano()
             ],
            extract: 'css/index.css'
        }) 
    ]
}
  • 重新打包

image.png

  • index.html需要额外引用打包后的css文件

rollup-plugin-vue

rollup-plugin-vue用于处理.vue文件。vue2和vue3项目所用的rollup-plugin-vue版本不一样,vue的编译器也不一样

  • vue2:rollup-plugin-vue^5.1.9 + vue-template-compiler

        yarn add rollup-plugin-vue@5.1.9 vue-template-compiler -D
    
  • vue3:rollup-plugin-vue^6.0.0 + @vue/compiler-sfc

        yarn add rollup-plugin-vue@6.0.0 @vue/compiler-sfc -D
    

这里以 vue2 为例子

//rollup.config.js
import vue from 'rollup-plugin-vue'
export default {
    input: ...,
    output: ..., 
    plugins:[ 
        vue()
    ]
}

新建一个src/component/hello/hello.vue文件

<template>
    <div class="hello">
        {{ msg }}
    </div>
</template>

<script>
export default {
    name: "hello",
    data() {
        return {
            msg: "我是 hello 组件",
        };
    },
};
</script>

<style lang="less" scoped>
.hello {
    color: red;
}
</style>
//src/index.js
import './css/index.css'
import Hello from './component/hello/hello.vue';

const install = (Vue)=>{
    Vue.component(Hello.name,Hello)
}

export default install
//index.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>
    <link rel="stylesheet" href="./dist/css/index.css">
</head>

<body>
    <div id="app">
        嘿嘿
        <hello></hello>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="./dist/my-temp-umd.js"></script>
</body>
<script>
    Vue.use(myTemp)
    new Vue({
        el: '#app'
    })
</script>

</html>

重新打包

image.png

rollup-plugin-vue也是默认支持scss、less、stylus,可以在项目中直接使用。给.vue文件中的css自动加前缀,需要在rollup-plugin-vue中配置。更多配置参考rollup-plugin-vue

//rollup.config.js
export default {
    input: ...,
    output: ..., 
    plugins:[ 
        vue({ 
            style: { 
                postcssPlugins: [ 
                    autoprefixer(), 
                    cssnano() 
                ] 
            } 
        }) 
    ]
}

rollup-plugin-terser

适用于ES6 +JavaScript解析器。rollup-plugin-uglify也能解析但是它只能翻译es5语法。如果要转译es6+语法,请改用terser

安装

    yarn add rollup-plugin-terser -D
//rollup.config.js
import {terser} from 'rollup-plugin-terser'
export default {
    input: ...,
    output: ..., 
    plugins:[ 
        terser() 
    ]
}

重新打包

image.png

@rollup/plugin-alias

一般用于解析第三方依赖或者对路径的别名

安装

    yarn add @rollup/plugin-alias -D

如果使用了 生产和开发环境都需要更换

//rollup.config.js
import alias from '@rollup/plugin-alias';
export default {
    input: ...,
    output: ..., 
    plugins:[ 
        alias({
            entries: [{
                find: '@',
                replacement: resolveDir('src')
            }]
        }) 
    ]
}

修改文件引入

//src/index.js
import '@/css/index.css'
import Hello from '@/component/hello/hello.vue';

const install = (Vue)=>{
    Vue.component(Hello.name,Hello)
}

export default install

rollup-plugin-serve、rollup-plugin-livereload

rollup-plugin-serve用于启动一个服务器,rollup-plugin-livereload用于文件变化时,实时刷新页面。

安装

    yarn add  rollup-plugin-livereload rollup-plugin-serve -D
//rollup.config.js
import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'
export default {
  ...
  plugins:[
    serve({
      contentBase: '',  //服务器启动的文件夹,默认是项目根目录,需要在该文件下创建index.html
      port: 8020   //端口号,默认10001
    }),    
    livereload('dist')   //watch dist目录,当目录中的文件发生变化时,刷新页面
  ]
}

image.png

我们在index.html的文件手动引入已经打包好的js、css文件。此时修改src中的文件,页面并不会实时刷新,因为dist目录下的文件并没有更新。 rollup监听源文件的改动很简单,就是在执行打包命令时,添加 -w 或者 --watch

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

实现按需加载的组件

  • 新建src/component/hello/index.js,新建src/component/test/index.js和src/component/test/test.vue
//src/component/hello/index.js
import Hello from '@/component/hello/hello.vue';

export default (Vue) => {
    Vue.component(Hello.name, Hello)
}
//src/component/test/index.js
import Test from '@/component/test/test.vue';

export default (Vue) => {
    Vue.component(Test.name, Test)
}
//src/component/test/test.vue
<template>
    <div class="test">
        {{ msg }}
    </div>
</template>

<script>
export default {
    name: "test",
    data() {
        return {
            msg: "我是 test 组件",
        };
    },
};
</script>

<style lang="less" scoped>
.test {
    color: blue;
}
</style>
//src.index.js
import '@/css/index.css'

import Hello from '@/component/hello';
import Test from '@/component/test';

const install = (Vue) => {
    Vue.use(Hello)
    Vue.use(Test)
}

export default install


export {
    Hello,
    Test
}

打包后修改package.json

"main": "./dist/my-lib-cjs.js",
"module": "./dist/my-lib-es.js",

本地调试组件

  • 将刚刚开发完的组件库 copy 到我们新建的vue项目的node_modules文件目录下 在文件夹里面运行npm link
  • 在项目的package.json修改
  "devDependencies": {
    "@babel/core": "^7.12.16",
    "@babel/eslint-parser": "^7.12.16",
    "@vue/cli-plugin-babel": "~5.0.0",
    "@vue/cli-plugin-eslint": "~5.0.0",
    "@vue/cli-service": "~5.0.0",
    "eslint": "^7.32.0",
    "eslint-plugin-vue": "^8.0.3",
    "vue-template-compiler": "^2.6.14",
    "my-temp": "^1.0.0" //这个名称就是 我们组件库的`package.json`文件的 name
   },
  • main.js
    import {Hello,Test} from "my-temp";
    import MyTemp from "my-temp";

    // console.log(Hello);
    // console.log(Test);
    console.log(MyTemp);

    Vue.use(Hello)
    Vue.use(Test)
    Vue.use(MyTemp)

  • app.vue
  <template>
    <div id="app">
        <!-- <img alt="Vue logo" src="./assets/logo.png" />
        <HelloWorld msg="Welcome to Your Vue.js App" /> -->
        <hello></hello>
        <test></test>
    </div>
</template>

<script>
// import HelloWorld from "./components/HelloWorld.vue";
export default {
    name: "App",
    components: {
        // HelloWorld,
    },
};
</script>

<style>
#app {
    font-family: Avenir, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
    margin-top: 60px;
}
</style>

-启动项目

image.png