网络日志

封装vue2.0组件(基于element-ui),并发布npm包

1、初始项目

mkdir vue-test & cd vue-test

2、安装包

npm init

一路回车,创建package.json,里面的name可以视情况自己更改

3、考虑通过webpack按需安装包(因为vue-cli安装了很多额外的包)

npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D

4、调整目录结构,新建assets、example、packages、src、webpack

assets存放静态资源,如images、less、scss、font等example存放示例packages存放即将开发的vue组件src存放启动项目的入口js等webpack存放不同配置

5、考虑webpack配置(1)vue项目,必然需要安装vue、vue-loader、vue-template-compiler,具体参考https://vue-loader.vuejs.org/(这里有一个注意点:需要安装vue@2和vue-loader@15,更高版本的话,就需要做vue3的处理了,目前尚未探索vue3的相关配置)(2)vue组件中包含了less、scss等,必然需要安装vue-style-loader、css-loader、less、less-loader、sass、sass-loader、node-sass(3)项目中有js文件,需要支持es6语法,必然需要安装babel-loader(此处还安装了@babel/preset-env,@babel/plugin-proposal-export-default-from,主要是为了做一些兼容处理)

6、在/webpack目录下,新建develop.js

const webpack = require('webpack')
const path = require('path')
const resolve = path.resolve
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  mode: 'development',
  mode: 'none',
  entry: resolve(__dirname, '../src/index'),
  output: {
    clean: true,
    filename: 'index.js'
  },
  module: {
    rules: [
      {
        test: /\.vue$/i,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],//webpack配置中提到
            plugins: ['@babel/plugin-proposal-export-default-from']//兼容export a from b语法
          }
        }
      },
      {
        test: /\.s[ac]ss$/,
        use: ['vue-style-loader', 'css-loader', 'sass-loader']
      },
      {
        test: /\.less$/,
        use: ['vue-style-loader', 'css-loader', 'less-loader']
      },
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader']
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource'
      }
    ]
  },
  devtool: 'inline-source-map',
  devServer: {
    static: './dist'
  },
  resolve: {
    alias: {
      '@': process.cwd()
    }
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      template: resolve(__dirname, '../src/index.html')
    })
  ]
}

具体配置,可以参考webpack官网,https://webpack.docschina.org...

7、新建入口文件要知道,webpack只是一个打包工具,根据入口js文件(即entry配置的内容),把import的各种资源,比如.vue文件或者图片等等整合到一起,最后以js文件输出(即配置output)。那么,我们需要到/src目录下,新建入口文件index.js、index.html、index.vue。(1)index.js(entry配置的入口文件)

import Vue from 'vue'
import App from "./index.vue"

new Vue({
  el: '#app',
  render: (h) => h(App)
})

这个js文件,大家应该很熟悉吧,跟vue-cli初始化的项目中,src/main.js是不是一样的?(2)index.html(主要是配合html-webpack-plugin组件)

<!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>
    <div id="app"></div>
  </body>
</html>

这个html文件,大家有没有感觉也很熟悉,跟vue-cli初始化的项目中,public/index.html是不是一样?(3)index.vue(vue项目的主文件)

<template>
  <div>this is my app</div>
</template>
<script>
export default {
  data(){
    return {}
  }
}
</script>
<style>
</style>

这不就是,src/App.vue 吗?这下终于明白,为啥vue-cli有这三个文件了,原来是为了配合webpack,当作项目的入口文件、

8、修改package.json

  "main": "dist/index.js",
  "scripts": {
    "dev": "webpack serve --open --config webpack/develop.js",
    "build": "webpack --config webpack/develop.js"
  },

主要是为了执行npm run dev和npm run build等

9、启动项目

npm run dev

(1)这时可以看到报错还需要安装@babel/core,之后重新执行npm run dev启动项目,浏览器会自动打开一个新的标签http://localhost:8081/

(2)浏览器打开开发者工具,F12里有个报错百度了一圈,https://stackoverflow.com/que...需要安装npm i process -D,并且webpack修改配置

 plugins: [
    ...
    new webpack.ProvidePlugin({
      process: 'process/browser'
    })
  ]

当然,好像更好的解决方式是安装cross-env

这时候,浏览器中应该已经出现内容了。

10、开发组件(以element-ui为基础)现在一个简单的vue已经跑起来了。后续写组件,大家应该轻车熟路了吧?

npm i element-ui

同时,修改index.js

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI)

在/packages目录下,新建你自己的组件,diy-int.vue,用来解决 input输入框 只能输入整数

<template>
  <span>
    <el-input
      v-model="inputVal"
      :placeholder="`请输入${$attrs.title || ''}`"
      v-bind="$attrs"
      @input="(val) => triggerInput($attrs, val)"
    >
      <template slot="prepend"><slot name="prepend"></slot></template>
      <template slot="append"><slot name="append"></slot></template>
    </el-input>
  </span>
</template>
<script>
export default {
  name: 'diy-int',
  model: {
    event: 'change',
    prop: 'value'
  },
  props: {
    value: { type: [Number, String], default: '' }
  },
  components: {},
  computed: {
    inputVal: {
      get () {
        return this.value
      },
      set (value) {
        this.$emit('change', value)
      }
    }
  },
  data () {
    return {
      visible: false
    }
  },
  methods: {
    triggerInput ($attrs, val) {
      let result = String(val)
        .replace(/[^\d]/g, '') // 只能填数字
        .replace(/^0+$/, '0') // 全部填写0时只显示一个0
        .replace(/^0(\d{1})/g, '$1') // 以0开头的整数,只展示整数部分;
      if ($attrs.max) {
        if (Number(result) > $attrs.max) {
          result = $attrs.max
        }
      }
      if ($attrs.min) {
        if (Number(result) < $attrs.min) {
          result = $attrs.min
        }
      }
      this.inputVal = result
    }
  },
  mounted () {}
}
</script>
<style lang="less" scoped></style>

去src/index.vue中引入组件


import DiyInt from "@/packages/diy-int.vue"

export default {
  ...
  components: {
    DiyInt
  },
  ...
}


<diy-int v-model="int" :max="100" :min="5">
  <el-button slot="append" icon="el-icon-edit"></el-button>
</diy-int>

你会发现,你自定义的一个组件就完成了。

11、通过vue.use()使用看看人家element-ui怎么使用的? Vue.use(ElementUI)vue提供了这个一样api使用自定义组件,具体参考https://cn.vuejs.org/v2/guide...那我们也需要调整一下在/packages目录下,新建index.js,统一管理该目录下的所有包

import DiyInt from "./diy-int.vue"
const install = (Vue) => {
  Vue.component('diy-int', DiyInt)
}
export default install

自此,就不再需要,每个vue页面中去import DiyInt from "@/packages/diy-int.vue",再通过components注册了,咱也可以直接在src/index.js中vue.use()了

import packages from "@/packages/index"
Vue.use(packages)

12、组件packages精简/packages/index.js中,现在只有一个组件,直接通过import的方式导入,觉得还行。假如等你开发了几十上百个组件之后,也通过这怎么import?感觉有点累吧。还好webpack提供更快捷的方式,require.context()调整一下/packages/index.js


const VueFiles = require.context('../packages', true, /\.vue$/)

const content = {}

VueFiles.keys().forEach((url) => {
  const vueFile = url.replace(/.*\/(.*)\.vue$/, '$1') // 文件名作为key,注意文件名的唯一性 (原谅我的正则比较拉胯,只能写出来这种)
  const vueFileDefault = VueFiles(url).default // export default 的文件内容
  content[vueFile] = vueFileDefault
})

const install = (Vue) => {
  for (const name in content) {
    Vue.component(name, content[name])
  }
}

export default install

以后,在packages中的vue文件,就会自动被引入,并且注册成为vue全局组件,咱就不用每个组件都去import了,省事。

再来对比一下element-ui

我去,原来你也可以比大厂的代码写得精简。(虽然我是一个菜鸡)

13、按需引入组件当然,有可能,咱的组件万一哪天拓展到几十上百个,都这样一次性引入?不太好吧。再去写轮眼一下element-ui???不对,借鉴一下element-ui,按需引入咋实现。

将packages/diy-int.vue变成文件夹

diy-int.comp.js单个引入此组件

import DiyInt from './diy-int.vue'

export default {
  install (Vue) {
    Vue.component(DiyInt.name, DiyInt)
  }
}

再调整一下/packages/index.js,将所有该目录下的组件导出

const VueFiles = require.context('../packages', true, /\.vue$/)
const JsFiles = require.context('../packages', true, /\.comp\.js$/)

const content = {}
const components = {}

VueFiles.keys().forEach((url) => {
  const vueFile = url.replace(/.*\/(.*)\.vue$/, '$1') // 文件名作为key
  const vueFileDefault = VueFiles(url).default // export default 的文件内容
  content[vueFile] = vueFileDefault
})

JsFiles.keys().forEach((url) => {
  const jsFile = url.replace(/.*\/(.*)\.comp\.js$/, '$1') // 文件名作为key
  const jsFileDefault = JsFiles(url).default // export default 的文件内容
  components[jsFile] = jsFileDefault
})

const install = (Vue) => {
  for (const name in content) {
    Vue.component(name, content[name])
  }
}
export default install
export { content, components }

如此,src/index.js中,就可实现按需加载了

import {components as DiyComponents} from "@/packages/index"
Vue.use(DiyComponents['diy-int'])
Vue.use(DiyComponents['other'])

14、规范问题,eslint咱开发组件,还是注意一下代码格式,结合现成的格式化插件,保持代码的美观。

npm i eslint prettier -D

之后,在命令行输入

npx eslint --init 或者 npm init @eslint/config

从命令行选择eslint相关的配置,就可以在项目根目录下生成.eslintrc.js

vscode推荐安装使用prettier-eslint,可以自动格式化代码,使其符合eslint规范再配置一下vscode,让每次保存文件时,自动格式代码

15、执行npm run build并发布npm执行

npm run build

将组件打包,生成dist目录(package.json中的main指向的就是dist/index.js)修改package.json中的name,改为你自己的组件名发布到npm

npm login

输入帐号密码邮箱,以及邮箱验证码登录npm帐号

npm publish

发布组件登录https://www.npmjs.com/,搜索你的组件名,就可以找到对应的组件。

以后的vue项目

npm i yourPackageName

你就可以直接安装你的包,并在main.js中vue.use()了