简易Vue ui 组件库笔记

817 阅读3分钟

vue 简易ui组件库随记

组件库官网

源码地址

github许可

进入仓库>点击create newfile >输入LICENSE

1651976242661.png

使用css变量

 //:root为根元素
:root{
    --button-height: 32px;
    --font-size: 14px;
    --button-bg: white;
    --button-active-bg: #eee;
    --border-radius: 4px;
    --color: #333;
    --border-color: #999;
    --border-color-hover: #666;
}

开发者使用-D,用户不用-D

vue安装

运行时 + 编译器 vs. 只包含运行时

如果你需要在客户端编译模板 (比如传入一个字符串给 template 选项,或挂载到一个元素上并以其 DOM 内部的 HTML 作为模板),就将需要加上编译器,即完整版:

// 需要编译器
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 不需要编译器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

当使用 vue-loadervueify 的时候,*.vue 文件内部的模板会在构建时预编译成 JavaScript。你在最终打好的包里实际上是不需要编译器的,所以只用运行时版本即可。

因为运行时版本相比完整版体积要小大约 30%,所以应该尽可能使用这个版本。如果你仍然希望使用完整版,则需要在打包工具里配置一个别名:

webpack

module.exports = {
  // ...
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'
    }
  }
}

Rollup

const alias = require('rollup-plugin-alias')

rollup({
  // ...
  plugins: [
    alias({
      'vue': require.resolve('vue/dist/vue.esm.js')
    })
  ]
})

Browserify

添加到你项目的 package.json

{
  // ...
  "browser": {
    "vue": "vue/dist/vue.common.js"
  }
}

Parcel

在你项目的 package.json 中添加:

{
  // ...
  "alias": {
    "vue" : "./node_modules/vue/dist/vue.common.js"
  }
}

运行

parcel index.html --no-cache

caniuse是什么?

caniuse官网

使用此工具,可以快速查看网站上的特定技术是否与特定浏览器兼容的问题,可帮助您更轻松地做出设计和开发决策。该网站主要由Alexis Deveria建立和维护, Lennart Schoors设计。CanIUse是一个开源项目,任何人都可以有助于为促进信息的数据库,甚至小的努力。

使用该工具很简单。通过对属性、参数或功能的简单搜索,caniuse.com将准确地告诉您哪些浏览器和版本支持该技术。 CanIUse还可以让您了解您正在寻找的属性的变体。这样你就知道哪些浏览器支持哪些类型的媒体查询。

以及到什么程度。兼容性图表不仅表明浏览器是否支持该技术,还表明是否支持某些版本的部分兼容性。

BDD 行为驱动开发

TDD 测试驱动开发

Assert 断言

单元测试

使用chailjs查看网址

mock,,使用chai spies

行内块元素 对不齐解决方案

*{display: inline-flex;}

felx布局

//order改变顺序子元素
 .icon { order: 2; margin-right: 0; margin-left: .1em;}

vue的position

export default {
  props:{
    iconName:{},
    iconPosition:{
      type:String,
      default:"left",
        //validator:检测外部数据的值
      validator(value){
        console.log(value)
      }
    }
  }
}

:**

控制台输出警告

console.log.warn()

单元测试

安装(chai)查看chai

npm i -D chai@4.1.2
使用 chai.expect 添加四个测试用例
/单元测试
import chai from 'chai'
const expect = chai.expect
{
    const Constructor = Vue.extend(Button)
    const vm = new Constructor({
        propsData: {
            icon: 'setup'
        }
    })
    vm.$mount()
    const useElement = vm.$el.querySelector('use')
    const href = useElement.getAttribute('xlink:href')
    expect(href).to.eq('#i-setup')
    vm.$el.remove()
    vm.$destroy()
}
{
    const Constructor = Vue.extend(Button)
    const vm = new Constructor({
        propsData: {
            icon: 'setup',
            loading:true
        }
    })
    vm.$mount()
    const useElement = vm.$el.querySelector('use')
    const href = useElement.getAttribute('xlink:href')
    expect(href).to.eq('#i-loading')
    vm.$el.remove()
    vm.$destroy()
}
{
    const div = document.createElement('div')
    document.body.appendChild(div)
    const Constructor = Vue.extend(Button)
    const vm = new Constructor({
        propsData: {
            icon: 'setup',
        }
    })
    vm.$mount(div)
    const svg = vm.$el.querySelector('svg')
    const {order} = window.getComputedStyle(svg)
    expect(order).to.eq('1')
    vm.$el.remove()
    vm.$destroy()
}
{
    const div = document.createElement('div')
    document.body.appendChild(div)
    const Constructor = Vue.extend(Button)
    const vm = new Constructor({
        propsData: {
            icon: 'setup'
        }
    })
    vm.$mount(div)
    const svg = vm.$el.querySelector('svg')
    const {order} = window.getComputedStyle(svg)
    expect(order).to.eq('2')
    vm.$el.remove()
    vm.$destroy()
}
{
    const div = document.createElement('div')
    document.body.appendChild(div)
    const Constructor = Vue.extend(Button)
    const vm = new Constructor({
        propsData: {
            icon: 'setup',
            iconPosition:'right'
        }
    })
    vm.$mount(div)
    vm.$on('click',()=>{
        console.log()
    })

}
测试click的执行函数 安装chai的常见

安装(chai- spies )

npm i -D chai-spies@1.0.0

测试click执行函数

import chai from 'chai'
import spies from "chai-spies"
chai.use(spies)
const expect = chai.expect
{
    const Constructor = Vue.extend(Button)
    const vm = new Constructor({
        propsData: {
            icon: 'setup',

        }
    })
    vm.$mount()
    //模拟事件执行函数
    let spy = chai.spy(function (){})
    vm.$on('click',spy)
    const button = vm.$el
    button.click()
    expect(spy).to.have.been.called()
}

自动化测试

使用 Karma + Mocha做单元测试
  1. Karma([ˈkɑrmə] 卡玛)是一个测试运行器,它可以呼起浏览器,加载测试脚本,然后运行测试用例
  2. Mocha([ˈmoʊkə] 摩卡)是一个单元测试框架/库,它可以用来写测试用例
  3. Sinon(西农)是一个 spy / stub / mock 库,用以辅助测试(使用后才能理解)

步骤

安装各种工具

npm i -D karma karma-chrome-launcher karma-mocha karma-sinon-chai mocha sinon sinon-chai karma-chai karma-chai-spies

创建 karma 配置

 // 新建 karma.conf.js,内容如下
 module.exports = function (config) {
     config.set({

         // base path that will be used to resolve all patterns (eg. files, exclude)
         basePath: '',
            // frameworks to use
            // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
            frameworks: ['mocha', 'sinon-chai'],
            client: {
                chai: {
                    includeStack: true
                }
            },


            // list of files / patterns to load in the browser
         //测试用例的文件,一个*是一层文件,**就是所有的不管多少层 
         files: [
                'dist/**/*.test.js',
                'dist/**/*.test.css'
            ],


            // list of files / patterns to exclude
            exclude: [],


            // preprocess matching files before serving them to the browser
            // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
            preprocessors: {},


            // test results reporter to use
            // possible values: 'dots', 'progress'
            // available reporters: https://npmjs.org/browse/keyword/karma-reporter
            reporters: ['progress'],


            // web server port
            port: 9876,


            // enable / disable colors in the output (reporters and logs)
            colors: true,


            // level of logging
            // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
            logLevel: config.LOG_INFO,


            // enable / disable watching file and executing tests whenever any file changes
            autoWatch: true,


            // start these browsers
            // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
         //要打开的浏览器
            browsers: ['ChromeHeadless'],


            // Continuous Integration mode
            // if true, Karma captures browsers, runs the tests and exits
            singleRun: false,

            // Concurrency level
            // how many browser should be started simultaneous
            concurrency: Infinity
        })
    }

创建 test/button.test.js 文件

 const expect = chai.expect;
 import Vue from 'vue'
 import Button from '../src/button'

 Vue.config.productionTip = false
 Vue.config.devtools = false

 describe('Button', () => {
     it('存在.', () => {
         expect(Button).to.be.ok
     })
     it('可以设置icon.', () => {
         const Constructor = Vue.extend(Button)
         const vm = new Constructor({
         propsData: {
             icon: 'settings'
         }
         }).$mount()
         const useElement = vm.$el.querySelector('use')
         expect(useElement.getAttribute('xlink:href')).to.equal('#i-settings')
         vm.$destroy()
     })
     it('可以设置loading.', () => {
         const Constructor = Vue.extend(Button)
         const vm = new Constructor({
         propsData: {
             icon: 'settings',
             loading: true
         }
         }).$mount()
         const useElements = vm.$el.querySelectorAll('use')
         expect(useElements.length).to.equal(1)
         expect(useElements[0].getAttribute('xlink:href')).to.equal('#i-loading')
         vm.$destroy()
     })
     it('icon 默认的 order 是 1', () => {
         const div = document.createElement('div')
         document.body.appendChild(div)
         const Constructor = Vue.extend(Button)
         const vm = new Constructor({
         propsData: {
             icon: 'settings',
         }
         }).$mount(div)
         const icon = vm.$el.querySelector('svg')
         expect(getComputedStyle(icon).order).to.eq('1')
         vm.$el.remove()
         vm.$destroy()
     })
     it('设置 iconPosition 可以改变 order', () => {
         const div = document.createElement('div')
         document.body.appendChild(div)
         const Constructor = Vue.extend(Button)
         const vm = new Constructor({
         propsData: {
             icon: 'settings',
             iconPosition: 'right'
         }
         }).$mount(div)
         const icon = vm.$el.querySelector('svg')
         expect(getComputedStyle(icon).order).to.eq('2')
         vm.$el.remove()
         vm.$destroy()
     })
     it('点击 button 触发 click 事件', () => {
         const Constructor = Vue.extend(Button)
         const vm = new Constructor({
         propsData: {
             icon: 'settings',
         }
         }).$mount()

         const callback = sinon.fake();
         vm.$on('click', callback)
         vm.$el.click()
         expect(callback).to.have.been.called

     })
 })

创建测试脚本

在 package.json 里面找到 scripts 并改写 scripts

 "scripts": {
     "dev-test": "parcel watch test/* --no-cache & karma start",
     //打包test下的以及文件,不要缓存,不要压缩
     //使用karma打开测试用例,配置文件是新建 karma.conf.js,内容如下
     "test": "parcel build test/* --no-minify && karma start --single-run"
 },

运行测试脚本

要么使用 npm run test 一次性运行

1652024276176.png

要么使用 npm run dev-test 进行 watch 运行

1652024315199.png

效果

如此一来,在开发的时候新开一个命令行窗口运行 npm run dev-test 就可以实时查看测试结果。 如果只想看一次结果,就只用运行 npm run test

1652025694177.png

持续集成测试(.travis )

根目录下创建

//新建.travis.yml文件
language: node_js
node_js:
  - "8"
addons:
  chrome: stable
sudo: required
before_script:
  - "sudo chown root /opt/google/chrome/chrome-sandbox"
  - "sudo chmod 4755 /opt/google/chrome/chrome-sandbox"

测试ui库

创建vue项目是报错

ERROR  Error: spawn yarn ENOENT
Error: spawn yarn ENOENT
spawn yarn ENOENT 
这是因为yarn找不到,而且设置的默认包管理是yarn
可以修改成npm
方式:一、直接在C盘用户名/.vuerc文件进行修改,将  "packageManager"改为"npm"
{
  "useTaobaoRegistry": false,
  "packageManager": "npm"
}

发布 npm 包

1 确保你的代码测试通过了

 npm run test 全部是绿色(原谅色)才行。 

2 上传代码到 npmjs.org

  1. 更新 package.json

    1. 在 package.json 里将版本号改为 0.0.1
    {
     "name": "golong-ui",
     "version": "0.0.9",
    }
    
    1. 创建 index.js,在 index.js 里将你想要导出的内容全部导出
    //上传的文件目录 
    "files": [
        "dist/*"
      ],
    //文件入口
      "main": "dist/index.js",
    
  2. www.npmjs.com/ 注册一个账户

  3. 确认一下邮箱(必须)

  4. 在 gulu 项目根目录运行 npm adduser 或者 npm login 登录npm

    • 如果错误提示里面含有 registry.npm.taobao.org 则说明你的 npm 源目前为淘宝源,需要更换为 npm 官方源
  5. 运行 npm publish:上传

使用自己的包

  1. 预测其他使用你的包的人会怎么使用
    • 使用 vue-cli
    • 使用 webpack
    • 使用 parcel
  2. 分别使用这些方式来使用自己的包(我们只以 vue-cli 为例)
    1. 使用过程中我们发现报错说 import 语法有问题,那时因为 node 目前确实不支持 import,我们需要用 babel 转译 import
      1. 你可以要求使用者自己用 babel 来转译
      2. 你也可以转义好了再给他用
        • npx parcel build index.js --no-minify (本来不应该加 --no-minify 的,奈何不加会有 bug,HTML 压缩把 slot 标签全删了)
        • 将 package.json 的 main 改为 dist/index.js

本地测试不用发布

  1. 使用 npm link 或者 yarn link 来加速调试
    1. 你每次修改源码之后,有两条路让别人得到最新效果
      1. 更新 package.json 里的 version,然后 npm publish。别人通过 npm update xxx 来更新。
      2. 如果你只是为了本地调试,可以在项目目录使用 npm link,然后在使用之处运行 npm link xxx,就是最新了

图标(shields)

git branch(创建爱你分支)

git checkout(切换分支)

scss for Each的使用

@for $n from 1 through 24 {
    &.#{$class-prefix}#{$n} {
      width: $n/24*100%;
    }
  }

什么代码需要重构

1 用到两次以上的代码

2一眼看,看不同的代码

scss不能用除法?

img

解决方法:

vue动画的简单用法

<template>
  <transition name="g-aside">
    <div class="g-aside" v-if="has">
      <slot></slot>
      <button @click="has=false">点击</button>
    </div>
  </transition>
</template>

<script lang="ts">
export default {
  name: "Aside",
  data(){
    return{
      has:true
    }
  }
};
</script>

<style lang="scss" scoped>
.g-aside{

}
.g-aside-enter-active, .g-aside-leave-active {
  transition: margin 1s;
}
.g-aside-enter, .g-aside-leave-to{
  margin-left: -150px;
}
</style>

Vue插件

插件

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

  1. 添加全局方法或者 property。如:vue-custom-element
  2. 添加全局资源:指令/过滤器/过渡等。如 vue-touch
  3. 通过全局混入来添加一些组件选项。如 vue-router
  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  5. 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router

使用插件

通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成:

// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)

new Vue({
  // ...组件选项
})

也可以传入一个可选的选项对象:

Vue.use(MyPlugin, { someOption: true })

Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。

Vue.js 官方提供的一些插件 (例如 vue-router) 在检测到 Vue 是可访问的全局变量时会自动调用 Vue.use()。然而在像 CommonJS 这样的模块环境中,你应该始终显式地调用 Vue.use()

// 用 Browserify 或 webpack 提供的 CommonJS 模块环境时
var Vue = require('vue')
var VueRouter = require('vue-router')

// 不要忘了调用此方法
Vue.use(VueRouter)

开发插件

Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:

MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或 property
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }

  // 2. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

  // 3. 注入组件选项
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })
  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
}

项目中创建的插件

//MyPlugin.js文件
import Toast from "./Toast";
let currentToast
export default {
    install(Vue,options){
        Vue.prototype.$toast =(message,toastOptions)=>{
            if(currentToast){
                currentToast.clear()
            }
            currentToast=createToast({Vue,message,propsData:toastOptions})
        }
    }
}

使用插件

//引入MyPlugin.js
import MyPlugin from "./MyPlugin";
//使用
Vue.use(MyPlugin)