最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 移动端组件库构建指北

    正文概述 掘金(bobo)   2021-01-01   640

    移动端组件库构建指北

    移动端组件库构建指北

    构建组件库的目录结构

    │  .gitlab-ci.yml(使用gitlab持续集成持续部署)
    │  babel.config.js(babel配置文件)
    │  package.json
    │  postcss.config.js(postcss预处理配置文件)
    │  README.md(说明文档)
    ├─build(构建配置目录)
    │  │  build-pkg.js(构建组件库的入口,生成lib文件夹)
    │  │  build-site.js(构建文档官网入口)
    │  │  createCpt.js(使用命令行生成待开发的组件)
    │  │  dev-server.js(本地开发入口)
    │  │  test-server.js(本地测试生成的组件库lib)
    │  ├─configs(webpack配置文档)
    │  │      package-all.config.js(全量构建组件库配置文件)
    │  │      package-base.config.js(构建组件库的基础配置文件)
    │  │      package-disperse.config.js(分包构建组件库的配置文件,提供按需加载)
    │  │      site-base.config.js(构建文档官网的基础配置文件)
    │  │      site-dev.config.js(构建文档官网的开发配置文件)
    │  │      site-prod.config.js(构建文档官网的生产配置文件)
    │  │      test-lib.config.js(测试生成的lib的配置文件)
    │  │      test-unit.config.js(单元测试配置文件)
    │  ├─test(单元测试启动入口)
    │  │      setup.js
    │  └─utils(工具目录)
    │          check-version.js(检测node npm版本)
    │          dic.js(目录函数)
    │          ip.js(获取本地ip)        
    ├─docs(文档官网的指南部分的md文档)
    │      i18n.md
    │      intro.md
    │      lazy.md
    │      theme.md
    ├─plugin(插件目录)
    │  └─mdToVue(md转vue文件)
    │          contrast.js
    │          index.js  
    ├─lib(最终生成的组件库目录,使用npm publish发布到npm)
    ├─site(文档官网,一个多入口的vue应用)
    │  ├─demo(文档官网的demo部分,模拟的手机)
    │  │      app.vue
    │  │      index.html
    │  │      index.js
    │  │      router.js    
    │  └─doc(文档官网的主体部分)
    │      │  app.vue
    │      │  favicon.ico
    │      │  index.html
    │      │  index.js
    │      │  left-nav.vue
    │      │  md.less
    │      │  root.js
    │      │  router.js
    │      ├─assets
    │      │      code.png
    │      │      copy.png
    │      │      qrcode.png    
    │      ├─page
    │      │      i18n.vue
    │      │      intro.vue
    │      │      lazy.vue
    │      │      theme.vue   
    │      └─view
    │              button.vue
    ├─src(组件库源码)
    │  │  config.json
    │  │  index.js
    │  │  index.less
    │  ├─assets(静态文件目录)   
    │  ├─locale(国际化)
    │  │  │  index.js 
    │  │  └─lang
    │  │          en-US.js
    │  │          zh-CN.js         
    │  ├─mixins(混入)
    │  │      bem.js(使用bem方式构建样式)
    │  │      i18n.js(国际化啊入口)
    │  ├─packages(组件目录)
    │  │  ├─button
    │  │  │  │  button.jsx
    │  │  │  │  demo.vue
    │  │  │  │  doc.md
    │  │  │  │  index.js
    │  │  │  │  index.less
    │  │  │  │  
    │  │  │  └─__test__
    │  │  │          button.spec.js        
    │  ├─style(基础样式)
    │  │      base.less
    │  │      hairline.less
    │  │      var.less   
    │  └─utils(工具库目录)
    │          constant.js
    │          deep-assign.js
    │          index.js
    │          
    └─test(测试构建lib的项目)
    

    构建具体步骤

    使用npm run dev启动开发(site文件夹)

    • 构建入口build/dev-server.js
    • doc部分:md转化成vue,作为site/doc依赖
      • docs/*.md => site/doc/page/*.vue
      • src/packages/**/*.md => site/doc/view/*.vue
    • demo部分:会直接引用src里面的组件作为依赖
      //多页面构建入口 site-dev.config.js
      entry:{
        demo: ROOT_PATH('site/demo/index.js'),
        doc: ROOT_PATH('site/doc/index.js')
      },
      output:{
        filename:'js/[name].bundle.js',
        chunkFilename:'js/[name].chunk.js'
      },
      new mdVue({
        entry:ROOT_PATH('docs'),
        output:ROOT_PATH('site/doc/page'),
        cache: false,
        needCode: false
      }),
      new mdVue({
        entry:ROOT_PATH('src'),
        output:ROOT_PATH('site/doc/view'),
        cache: false
      }),
    

    使用npm run site构建生产环境的dist包

    • 使用site-prod.config.js将site目录代码转换成可部署到生产环境的dist包

    使用npm run pkg构建组件库

    • 全量构建 package-all.config.js
      entry: {
        zmmui: ROOT_PATH('src/index.js')
      },
      output:{
        path:ROOT_PATH('lib'),
        filename:'zmmui.js',
        library:'[name]',
        libraryTarget:'umd',
        umdNamedDefine: true,
        globalObject: 'this'
      },
    
    • 分包构建 package-disperse.config.js
      const entry = {}
      confs.packages.map((item) => {
        let cptName = item.name.toLowerCase()
        entry[cptName] = ROOT_PATH(`src/packages/${cptName}/index.js`)
      })
    
      entry: entry,
      output:{
        path:ROOT_PATH('lib/packages'),
        filename:'[name]/[name].js',
        library:'[name]',
        libraryTarget:'umd',
        umdNamedDefine: true,
        globalObject: 'this'
      },
    

    MardDown文件转化成vue文件的webpack插件

      //遍历文件夹下面的文件
      const nodeFilelist = require('node-filelist');
      // 高亮插件
      const hljs = require('highlight.js');
      // markdown转换成html标签
      let marked = require('marked');
      // 监听文件变化
      let Chokidar = require('chokidar');
      // 计算目录文件的hash
      let { hashElement } = require('folder-hash');
    
    let rendererMd = new marked.Renderer()
    rendererMd.heading = (text,level) => {
    	// 标题标签的处理逻辑
    }
    rendererMd.code = (code, infostring) => {
    	// code代码的处理逻辑
    }
    marked.setOptions({
      tables: true,
      renderer: this.rendererMd,
    });
    
    • mdToVue/contrast.js:通过计算缓存文件的hash值,当文件变化时计算并返回需要转换的md文件
    • 当渲染器的heading处理逻辑发生变化时,必须要使用marked.setOptions重置渲染器

    按需加载插件babel-plugin-sep-import

    // 用来生成一个特定类型的ast语法
    const t = require('@babel/types')
    
    module.exports = function() {
      return {
        visitor: {
          //遍历import语法
          ImportDeclaration(p, {opts = {}}) {
    
            let libraryName = opts.libraryName || 'zmmui'
            let libraryDir = opts.libraryDir || 'lib/packages'
            let style = opts.style || 'css'
    
            const {node} = p;
            // 如果节点的value值等于目标的libraryName,进行下一步操作
            if(node.source.value && node.source.value == libraryName){
              node.specifiers.forEach((item) => {
                let cpt = item.imported.name
                // 节点前插入处理
                p.insertBefore(
                  // 根据规则生成一个新的import语法
                  t.importDeclaration(
                    [t.importDefaultSpecifier(t.identifier(cpt))],
                    t.stringLiteral(`${libraryName}/${libraryDir}/${cpt.toLowerCase()}/index.js`)
                  )
                )
                if(style === 'css'){
                  p.insertBefore(t.importDeclaration([], t.stringLiteral(`${libraryName}/${libraryDir}/${cpt.toLowerCase()}/${cpt.toLowerCase()}.css`)));
                } else {
                  p.insertBefore(t.importDeclaration([], t.stringLiteral(`${libraryName}/${libraryDir}/${cpt.toLowerCase()}/index.${style}`)));
                }
              })
              // 删除原节点
              p.remove()
            }
          }
        }
      }
    }
    
    

    使用npm run cpt生成一个新的组件

    const inquirer = require('inquirer') 命令行交互工具,保存用户输入
    const parse = require('@babel/parser').parse 将js代码转换成抽象语法树
    const traverse = require('@babel/traverse').default 遍历生成的抽象语法树
    const generate = require('@babel/generator').default 将修改后的抽象语法树生成新的代码
    

    自动化生成新的组件,以及动态修改、添加入口文件依赖项

    单元测试以及代码覆盖率(npm run test:unit)

    cross-env NODE_ENV=test nyc mochapack --webpack-config build/configs/test-unit.config.js --require build/test/setup.js src/packages/**/__test__/*.js --reporter=mochawesome
    
    • --require build/test/setup.js 确保运行单元测试前使用setup提供测试环境
    • --reporter=mochawesome 优化单元测试报告的输出
    • mochapack mocha 与 webpack 结合的单元测试工具
    • nyc 计算测试覆盖率
    • jsdom-global node里面模拟jsdom
    • 单元测试配置文件test-unit.config.js
    rules: [
        {
            test: /\.js$|\.jsx$/,
            use: [
                'babel-loader',
                {
                	// 使用istanbul-instrumenter-loader插入代码用于计算覆盖率
                    loader: 'istanbul-instrumenter-loader',
                    options: { esModules: true },
                }
            ],
            include: [ROOT_PATH('src/packages')],
        },
     ...
    

    使用这种方式计算覆盖率时,如果使用sfc的方式编写组件库,覆盖率一直是0,因此使用了jsx方式编写组件库,能正常计算出代码覆盖率

    总结

    • 通过这个项目学习了webpack的配置,babel插件的写法、ast语法树相关、webpack插件、单元测试以及测试覆盖率
    • 祝大家元旦快乐!

    起源地下载网 » 移动端组件库构建指北

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元