jczzq

jczzq 查看完整档案

上海编辑西南交通大学  |  计算机与科学 编辑KB  |  前端开发工程师 编辑 www.jczzq.com 编辑
编辑

死宅程序员

个人动态

jczzq 发布了文章 · 2020-07-25

Flutter开发笔记

一直想尝试从头开发一款自己的App,最后决定选择flutter

Github地址:https://github.com/jczzq/pulse

Windows下安装与环境配置

按照官网教程安装,非常详细。
https://flutter.cn/docs/get-s...
安装过程中可能会遇到的一些小问题:

1. flutter pub get 下载依赖时卡住

设置下面两个环境变量:

这一步操作类似于给npm设置淘宝镜像源加速。

2. 启动调试时卡在 Running Gradle task 'assembleDebug'... 这一步

image.png
是因为gradle加载失败.

可以手动下载指定版本的gradle:
https://downloads.gradle-dn.c...
然后拷贝到指定目录:
C:\Users\11952\.gradle\wrapper\dists\gradle-5.6.2-all\9st6wgf78h16so49nn74lgtbb

3. 卡在 Waiting for another flutter command to release the startup lock

flutter安装目录的cache目录下删除lockfile文件.
C:\flutter\bin\cache\lockfile

4. 卡在 Installing build\app\outputs\apk\app.apk...

开发

使用外部包

https://flutter.cn/docs/get-s...
可以在 https://pub.flutter-io.cn/ 中找需要的包。

查看原文

赞 0 收藏 0 评论 0

jczzq 赞了文章 · 2019-09-16

前端黑科技:美团网页首帧优化实践

前言

自JavaScript诞生以来,前端技术发展非常迅速。移动端白屏优化是前端界面体验的一个重要优化方向,Web 前端诞生了 SSR 、CSR、预渲染等技术。在美团支付的前端技术体系里,通过预渲染提升网页首帧优化,从而优化了白屏问题,提升用户体验,并形成了最佳实践。

在前端渲染领域,主要有以下几种方式可供选择:

CSR预渲染SSR同构
优点不依赖数据FP 时间最快客户端用户体验好内存数据共享不依赖数据FCP 时间比 CSR 快客户端用户体验好内存数据共享SEO 友好首屏性能高,FMP 比 CSR 和预渲染快SEO 友好首屏性能高,FMP 比 CSR 和预渲染快客户端用户体验好内存数据共享客户端与服务端代码公用,开发效率高
缺点SEO 不友好FCP 、FMP 慢SEO 不友好FMP 慢客户端数据共享成本高模板维护成本高Node 容易形成性能瓶颈

通过对比,同构方案集合 CSR 与 SSR 的优点,可以适用于大部分业务场景。但由于在同构的系统架构中,连接前后端的 Node 中间层处于核心链路,系统可用性的瓶颈就依赖于 Node ,一旦作为短板的 Node 挂了,整个服务都不可用。

结合到我们团队负责的支付业务场景里,由于支付业务追求极致的系统稳定性,服务不可用直接影响到客诉和资损,因此我们采用浏览器端渲染的架构。在保证系统稳定性的前提下,还需要保障用户体验,所以采用了预渲染的方式。

那么究竟什么是预渲染呢?什么是 FCP/FMP 呢?我们先从最常见的 CSR 开始说起。

以 Vue 举例,常见的 CSR 形式如下:

一切看似很美好。然而,作为以用户体验为首要目标的我们发现了一个体验问题:首屏白屏问题

为什么会首屏白屏

浏览器渲染包含 HTML 解析、DOM 树构建、CSSOM 构建、JavaScript 解析、布局、绘制等等,大致如下图所示:

要搞清楚为什么会有白屏,就需要利用这个理论基础来对实际项目进行具体分析。通过 DevTools 进行分析:

  • 等待 HTML 文档返回,此时处于白屏状态。
  • 对 HTML 文档解析完成后进行首屏渲染,因为项目中对 id="app加了灰色的背景色,因此呈现出灰屏。
  • 进行文件加载、JS 解析等过程,导致界面长时间出于灰屏中。
  • 当 Vue 实例触发了 mounted 后,界面显示出大体框架。
  • 调用 API 获取到时机业务数据后才能展示出最终的页面内容。

由此得出结论,因为要等待文件加载、CSSOM 构建、JS 解析等过程,而这些过程比较耗时,导致用户会长时间出于不可交互的首屏灰白屏状态,从而给用户一种网页很“慢”的感觉。那么一个网页太“慢”,会造成什么影响呢?

“慢”的影响

Global Web Performance Matters for ecommerce的报告中指出:

  • 57%的用户更在乎网页在3秒内是否完成加载。
  • 52%的在线用户认为网页打开速度影响到他们对网站的忠实度。
  • 每慢1秒造成页面 PV 降低11%,用户满意度也随之降低降低16%。
  • 近半数移动用户因为在10秒内仍未打开页面从而放弃。

我们团队主要负责美团支付相关的业务,如果网站太慢会影响用户的支付体验,会造成客诉或资损。既然网站太“慢”会造成如此重要的影响,那要如何优化呢?

优化思路

User-centric Performance Metrics一文中,共提到了4个页面渲染的关键指标:

基于这个理论基础,再回过头来看看之前项目的实际表现:

可见在 FP 的灰白屏界面停留了很长时间,用户不清楚网站是否有在正常加载,用户体验很差。

试想:如果我们可以将 FCP 或 FMP 完整的 HTML 文档提前到 FP 时机预渲染,用户看到页面框架,能感受到页面正在加载而不是冷冰冰的灰白屏,那么用户更愿意等待页面加载完成,从而降低了流失率。并且这种改观在弱网环境下更明显。

通过对比 FP、FCP、FMP 这三个时期 DOM 的差异,发现区别在于:



  • FP:仅有一个 div 根节点。
  • FCP:包含页面的基本框架,但没有数据内容。
  • FMP:包含页面所有元素及数据。

仍然以 Vue 为例, 在其生命周期中,mounted 对应的是 FCP,updated 对应的是 FMP。那么具体应该使用哪个生命周期的 HTML 结构呢?

mounted (FCP)updated (FMP)
缺点只是视觉体验将 FCP 提前,实际的 TTI 时间变化不大构建时需要获取数据,编译速度慢构建时与运行时的数据存在差异性有复杂交互的页面,仍需等待,实际的 TTI 时间变化不大
优点不受数据影响,编译速度快首屏体验好对于纯展示类型的页面,FP 与 TTI 时间近乎一致

通过以上的对比,最终选择在 mounted 时触发构建时预渲染。由于我们采用的是 CSR 的架构,没有 Node 作为中间层,因此要实现 DOM 内容的预渲染,就需要在项目构建编译时完成对原始模板的更新替换。

至此,我们明确了构建时预渲染的大体方案。

构建时预渲染方案

构建时预渲染流程:

配置读取

由于 SPA 可以由多个路由构成,需要根据业务场景决定哪些路由需要用到预渲染。因此这里的配置文件主要是用于告知编译器需要进行预渲染的路由。

在我们的系统架构里,脚手架是基于 Webpack 自研的,在此基础上可以自定义自动化构建任务和配置。

触发构建

项目中主要是使用 TypeScript,利用 TS 的装饰器,我们封装了统一的预渲染构建的钩子方法,从而只用一行代码即可完成构建时预渲染的触发。

装饰器:

使用:

构建编译

从流程图上,需要在发布机上启动模拟的浏览器环境,并通过预渲染的事件钩子获取当前的页面内容,生成最终的 HTML 文件。

由于我们在预渲染上的尝试比较早,当时还没有 Headless ChromePuppeteerPrerender SPA Plugin等,因此在选型上使用的是 phantomjs-prebuilt(Prerender SPA Plugin 早期版本也是基于 phantomjs-prebuilt 实现的)。

通过 phantom 提供的 API 可获得当前 HTML,示例如下:

为了提高构建效率,并行对配置的多个页面或路由进行预渲染构建,保证在 5S 内即可完成构建,流程图如下:

方案优化

理想很丰满,现实很骨感。在实际投产中,构建时预渲染方案遇到了一个问题。

我们梳理一下简化后的项目上线过程:

开发 -> 编译 -> 上线

假设本次修改了静态文件中的一个 JS 文件,这个文件会通过 CDN 方式在 HTML 里引用,那么最终在 HTML 文档中的引用方式是 <script data-original="http://cdn.com/index.js"></script>。然而由于项目还没有上线,所以其实通过完整 URL 的方式是获取不到这个文件的;而预渲染的构建又是在上线动作之前,所以问题就产生了:

构建时预渲染无法正常获取文件,导致编译报错

怎么办?

请求劫持

因为在做预渲染时,我们使用启动了一个模拟的浏览器环境,根据 phantom 提供的 API,可以对发出的请求加以劫持,将获取 CDN 文件的请求劫持到本地,从而在根本上解决了这个问题。示例代码如下:

构建时预渲染研发流程及效果

最终,构建时预渲染研发流程如下:

开发阶段:

  • 通过 TypeScript 的装饰器单行引入预渲染构建触发的方法。
  • 发布前修改编译构建的配置文件。

发布阶段:

  • 先进行常规的项目构建。
  • 若有预渲染相关配置,则触发预渲染构建。
  • 通过预渲染得到最终的文件,并完成发布上线动作。

完整的用户请求路径如下:

通过构建时预渲染在项目中的使用,FCP 的时间相比之前减少了 75%。

作者简介

寒阳,美团资深研发工程师,多年前端研发经历,负责美团支付钱包团队和美团支付前端基础技术。

招聘信息

我们美团金融服务平台大前端研发组在高速成长中,我们欢迎更多优秀的 Web 前端研发工程师加入,感兴趣的朋友可以将简历发送到邮箱:shanghanyang@meituan.com。

查看原文

赞 140 收藏 97 评论 16

jczzq 关注了用户 · 2019-05-14

Cinwell @cinwell

关注 39

jczzq 收藏了文章 · 2019-05-09

参考ElementUI的文档实现方案,实现自己组件库的说明文档

实现使用markdown编写的个人组件库说明文档

前一篇文章实现了按需加载封装个人的组件库功能,有了组件库,当然还要有配套说明文档,这样使者用起来才更方便。打包完成的dist目录是最终可放到服务器中,直接访问到文档的哟。
项目github地址:https://github.com/yuanalina/installAsRequired

在项目中配置打包examples

上篇文章中,执行打包命令会将项目打包至lib下,打包完成的目录结构是适合直接发布为npm包,使用时使用import等引入的。其中并没有html文件入口,所以要有说明文档,直接在浏览器中可访问,还需要重新配置打包。

打包examples相关目录结构及webpack配置

一、package.json增加打包命令"build_example": "node build/build.js examples"

clipboard.png

二、在src同级增加examples目录,存放文档相关文件

examples目录中:1、assets目录存放静态资源依赖,2、components存放vue组件,3、docs目录存放.md文件,说明文档,4、main.js会作为打包的入口,在这里引入项目的组件、第三方依赖:element-ui、路由配置等,5、route.js路由配置,6、index.html作为打包的html模版,7、App.vue

clipboard.png

三、webpack相关配置

在build目录中增加webpack.prod.examples.conf.js文件,配置打包example。这个文件是vue-cli生成项目中的webpack.prod.conf.js稍作修改,改动部分:

1、增加output出口配置,由于之前在config中将这个值设置成了../lib,这里把值设置为../dist,将examples打包后输出到dist

clipboard.png

2、设置打包入口为examples下的main.js

clipboard.png

3、设置html模版为./examples/index.html

clipboard.png

另外在build/build.js中,需要判断example参数,更改一下output出口路径,如图:

clipboard.png

技术实现

编写说明文档,最直观的还是使用markdown编写,看了elementUI的实现方案,决定按elementUI的技术方案去实现。特别说明:本文中有部分实现是copy了elementUI的代码实现的。后面会特别指出

相关依赖安装

npm i highlight -D //安装语法高亮
npm i markdown-it markdown-it-anchor markdown-it-container -D // 安装markdown相关依赖
npm i vue-markdown-loader -D //安装vue-markdown-loader,解析.md文件为.vue文件

webpack相关配置

安装了vue-markdown-loader解析.md文件,在webpack.base.conf.js文件中,需要进行相关的loader配置,这里的配置相关,都是copy的element-ui中的代码。改动部分如下:
一、首先增加strip-tags文件到/build目录中,strip-tags内容如下:

/*!
 * strip-tags <https://github.com/jonschlinkert/strip-tags>
 *
 * Copyright (c) 2015 Jon Schlinkert, contributors.
 * Licensed under the MIT license.
 */

'use strict';

var cheerio = require('cheerio');

exports.strip = function(str, tags) {
  var $ = cheerio.load(str, {decodeEntities: false});

  if (!tags || tags.length === 0) {
    return str;
  }

  tags = !Array.isArray(tags) ? [tags] : tags;
  var len = tags.length;

  while (len--) {
    $(tags[len]).remove();
  }

  return $.html();
};

exports.fetch = function(str, tag) {
  var $ = cheerio.load(str, {decodeEntities: false});
  if (!tag) return str;

  return $(tag).html();
};

二、webpack.base.conf.js的改动
1、增加引入strip-tags和markdown-it

const striptags = require('./strip-tags')
const md = require('markdown-it')()

2、增加工具函数

const wrap = function(render) {
  return function() {
    return render.apply(this, arguments)
      .replace('<code v-pre class="', '<code class="hljs ')
      .replace('<code>', '<code class="hljs">')
  }
}

function convert(str) {
  str = str.replace(/(&#x)(\w{4});/gi, function($0) {
    return String.fromCharCode(parseInt(encodeURIComponent($0).replace(/(%26%23x)(\w{4})(%3B)/g, '$2'), 16))
  })
  return str
}

3、增加.md相关loader配置,将.md文件解析为.vue文件,同时,解析处理::: demo :::代码块等,解析处理::: demo :::代码块为demo-block vue组件,并传入对应参数.

{
    test: /\.md$/,
    loader: 'vue-markdown-loader',
    options: {
      use: [
        [require('markdown-it-container'), 'demo', {
          validate: function(params) {
            return params.trim().match(/^demo\s*(.*)$/)
          },
    
          render: function(tokens, idx) {
            var m = tokens[idx].info.trim().match(/^demo\s*(.*)$/)
            if (tokens[idx].nesting === 1) {
              var description = (m && m.length > 1) ? m[1] : ''
              var content = tokens[idx + 1].content
              var html = convert(striptags.strip(content, ['script', 'style'])).replace(/(<[^>]*)=""(?=.*>)/g, '$1')
              var script = striptags.fetch(content, 'script')
              var style = striptags.fetch(content, 'style')
              var jsfiddle = { html: html, script: script, style: style }
              var descriptionHTML = description
                ? md.render(description)
                : ''
    
              jsfiddle = md.utils.escapeHtml(JSON.stringify(jsfiddle))
    
              return `<demo-block class="demo-box" :jsfiddle="${jsfiddle}">
                        <div class="source" slot="source">${html}</div>
                        ${descriptionHTML}
                        <div class="highlight" slot="highlight">`
            }
            return '</div></demo-block>\n'
          }
        }],
        [require('markdown-it-container'), 'tip'],
        [require('markdown-it-container'), 'warning']
      ],
      preprocess: function(MarkdownIt, source) {
        MarkdownIt.renderer.rules.table_open = function() {
          return '<table class="table">';
        };
        MarkdownIt.renderer.rules.fence = wrap(MarkdownIt.renderer.rules.fence)
        return source;
      }
    }
}

文档编写部分

配置相关的就告一段落了,接下来进入examples中的文档编写部分
一、main.js入口文件编写
在入口文件中,引入相关依赖,项目样式入口、路由、组件以及element-ui

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
// 引入组件
import JY from '../src'
Vue.use(JY)

// 引入element-ui
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI)

// 引入demo-block
import DemoBlock from './components/demoBlock'
Vue.component('demo-block', DemoBlock)
// 引入项目样式入口
import './assets/less/index.less'

// 引入路由
import routes from './route'
Vue.use(VueRouter)
const router = new VueRouter({
  routes
})
/* eslint-disable no-new */
new Vue({
  render(createElement) {
    return createElement(App)
  },
  router
}).$mount('#app')

二、设置路由配置route.js
路由配置时,将路由路径对应的组件设置为引入的.md文件

import Install from './docs/install.md'
import QuikeStart from './docs/quikeStart.md'
import Input from './docs/input.md'
const routes = [
  {
    path: '/',
    component: Install,
    name: 'default'
  },
  {
    path: '/guide/install',
    name: 'Install',
    component: Install
  },
  {
    path: '/guide/quikeStart',
    name: 'quikeStart',
    component: QuikeStart
  },
  {
    path: '/input',
    name: 'input',
    component: Input
  }
]

export default routes

三、App.vue、以及相关布局组件
1、App.vue

<template lang="html">
  <div style="height:100%">
    <el-container style="height:100%">
      <el-header height="40">
        <header-model></header-model>
      </el-header>
      <el-container>
        <el-aside width="200px">
          <menu-model></menu-model>
        </el-aside>
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<style>
/* 引入代码高亮样式 */
@import 'highlight.js/styles/color-brewer.css';
</style>

<script>
import HeaderModel from './components/header'
import MenuModel from './components/menu'
export default {
  components: {
    HeaderModel,
    MenuModel
  },
  data() {
    return {
    }
  },
  methods: {
  }
}
</script>

2、header.vue

<template lang="html">
  <div class="header-model">
    <h1 class="info">
      通用组件库
    </h1>
  </div>
</template>

<script>
export default {
  data () {
    return {
    }
  }
}
</script>

3、menu.vue

<template lang="html">
  <div class="menu-model">
    <el-menu
      default-active="1"
      :unique-opened="true"
      :default-openeds="['1', '2', '3']"
      :default-active="defaultActive"
      :router="true"
    >
      <el-submenu index="1">
        <template slot="title">
          <span>开发指南</span>
        </template>
        <el-menu-item-group>
          <el-menu-item index="/guide/install">安装</el-menu-item>
          <el-menu-item index="/guide/quikeStart">快速上手</el-menu-item>
        </el-menu-item-group>
      </el-submenu>
      <el-submenu index="2">
        <template slot="title">
          <span>通用模块</span>
        </template>
        <el-menu-item-group>
          <el-menu-item index="/input">Input</el-menu-item>
        </el-menu-item-group>
      </el-submenu>
    </el-menu>
  </div>
</template>

<script>
export default {
  data () {
    return {
      defaultActive: '/guide/install'
    }
  },
  created () {
    const path = this.$route.fullPath
    this.defaultActive = path == '/' ? '/guide/install' : path
  },
  methods: {
  }
}
</script>

<style lang="css">
</style>

四、重要组件demoBlock.vue
demoBlock组件是解析.md中的::: demo ::: 代码块需要用到的组件,这里的demoBlock.vue文件是copy的element-ui的代码后稍作修改

<template>
  <div
    class="demo-block"
    :class="[blockClass, { 'hover': hovering }]"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false">
    <slot name="source"></slot>
    <div class="meta" ref="meta">
      <div class="description" v-if="$slots.default">
        <slot></slot>
      </div>
      <slot name="highlight"></slot>
    </div>
    <div
      class="demo-block-control"
      ref="control"
      @click="isExpanded = !isExpanded">
      <transition name="arrow-slide">
        <i :class="[iconClass, { 'hovering': hovering }]"></i>
      </transition>
      <transition name="text-slide">
        <span v-show="hovering">{{ controlText }}</span>
      </transition>
    </div>
  </div>
</template>

<style lang="less">
  .demo-block {
    border: solid 1px #ebebeb;
    border-radius: 3px;
    transition: .2s;

    &.hover {
      box-shadow: 0 0 8px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .5);
    }

    code {
      font-family: Menlo, Monaco, Consolas, Courier, monospace;
    }

    .demo-button {
      float: right;
    }

    .source {
      padding: 24px;
    }

    .meta {
      background-color: #fafafa;
      border-top: solid 1px #eaeefb;
      overflow: hidden;
      height: 0;
      transition: height .2s;
    }

    .description {
      padding: 20px;
      box-sizing: border-box;
      border: solid 1px #ebebeb;
      border-radius: 3px;
      font-size: 14px;
      line-height: 22px;
      color: #666;
      word-break: break-word;
      margin: 10px;
      background-color: #fff;

      p {
        margin: 0;
        line-height: 26px;
      }

      code {
        color: #5e6d82;
        background-color: #e6effb;
        margin: 0 4px;
        display: inline-block;
        padding: 1px 5px;
        font-size: 12px;
        border-radius: 3px;
        height: 18px;
        line-height: 18px;
      }
    }

    .highlight {
      pre {
        margin: 0;
      }

      code.hljs {
        margin: 0;
        border: none;
        max-height: none;
        border-radius: 0;

        &::before {
          content: none;
        }
      }
    }

    .demo-block-control {
      border-top: solid 1px #eaeefb;
      height: 44px;
      box-sizing: border-box;
      background-color: #fff;
      border-bottom-left-radius: 4px;
      border-bottom-right-radius: 4px;
      text-align: center;
      margin-top: -1px;
      color: #d3dce6;
      cursor: pointer;
      position: relative;

      &.is-fixed {
        position: fixed;
        bottom: 0;
        width: 868px;
      }

      i {
        font-size: 16px;
        line-height: 44px;
        transition: .3s;
        &.hovering {
          transform: translateX(-40px);
        }
      }

      > span {
        position: absolute;
        transform: translateX(-30px);
        font-size: 14px;
        line-height: 44px;
        transition: .3s;
        display: inline-block;
      }

      &:hover {
        color: #409EFF;
        background-color: #f9fafc;
      }

      & .text-slide-enter,
      & .text-slide-leave-active {
        opacity: 0;
        transform: translateX(10px);
      }

      .control-button {
        line-height: 26px;
        position: absolute;
        top: 0;
        right: 0;
        font-size: 14px;
        padding-left: 5px;
        padding-right: 25px;
      }
    }
  }
</style>

<script type="text/babel">
  export default {
    data() {
      return {
        hovering: false,
        isExpanded: false,
        fixedControl: false,
        scrollParent: null,
        langConfig: {
          "hide-text": "隐藏代码",
          "show-text": "显示代码",
          "button-text": "在线运行",
          "tooltip-text": "前往 jsfiddle.net 运行此示例"
        }
      };
    },

    props: {
      jsfiddle: Object,
      default() {
        return {};
      }
    },

    methods: {
      scrollHandler() {
        const { top, bottom, left } = this.$refs.meta.getBoundingClientRect();
        this.fixedControl = bottom > document.documentElement.clientHeight &&
          top + 44 <= document.documentElement.clientHeight;
      },

      removeScrollHandler() {
        this.scrollParent && this.scrollParent.removeEventListener('scroll', this.scrollHandler);
      }
    },

    computed: {
      lang() {
        return this.$route.path.split('/')[1];
      },

      blockClass() {
        return `demo-${ this.lang } demo-${ this.$router.currentRoute.path.split('/').pop() }`;
      },

      iconClass() {
        return this.isExpanded ? 'el-icon-caret-top' : 'el-icon-caret-bottom';
      },

      controlText() {
        return this.isExpanded ? this.langConfig['hide-text'] : this.langConfig['show-text'];
      },

      codeArea() {
        return this.$el.getElementsByClassName('meta')[0];
      },

      codeAreaHeight() {
        if (this.$el.getElementsByClassName('description').length > 0) {
          return this.$el.getElementsByClassName('description')[0].clientHeight +
            this.$el.getElementsByClassName('highlight')[0].clientHeight + 20;
        }
        return this.$el.getElementsByClassName('highlight')[0].clientHeight;
      }
    },

    watch: {
      isExpanded(val) {
        this.codeArea.style.height = val ? `${ this.codeAreaHeight + 1 }px` : '0';
        if (!val) {
          this.fixedControl = false;
          this.$refs.control.style.left = '0';
          this.removeScrollHandler();
          return;
        }
        setTimeout(() => {
          this.scrollParent = document.querySelector('.page-component__scroll > .el-scrollbar__wrap');
          this.scrollParent && this.scrollParent.addEventListener('scroll', this.scrollHandler);
          this.scrollHandler();
        }, 200);
      }
    },

    mounted() {
      this.$nextTick(() => {
        let highlight = this.$el.getElementsByClassName('highlight')[0];
        if (this.$el.getElementsByClassName('description').length === 0) {
          highlight.style.width = '100%';
          highlight.borderRight = 'none';
        }
      });
    },

    beforeDestroy() {
      this.removeScrollHandler();
    }
  };
</script>

五、docs中的.md文档文件
.md文件编写时有几个需要注意的地方:
具有交互功能的说明文档,需要有<script></script>标签,在标签元素中定义需要导出的vue实例。
在:::demo ::: 代码块中定义的模版<template></template>会作为导出的vue实例的模版,但是在代码块中的<script></script>中的内容仅作为展示。

clipboard.png
.md文件粘贴进来会展示有误,这里只进行了截图,有需要的伙伴可以进入github查看
六、样式调整
样式相关的调整代码这里就不单独列出来说明了,需要的伙伴可以进入github查看

开发中的调试

设置webpack.dev.conf.js文件的入口为./examples/main.js,这样即可以边开发组件边调试,同时也可以调试到说明文档。

entry: {
    app: './examples/main.js'
  },

本文结束啦~希望对你有所帮助。。学无止境,与诸君共勉~~

查看原文

jczzq 发布了文章 · 2019-03-27

【mone-query】基于 element-ui 的通用查询组件

mone-query

clipboard.png

Github: https://github.com/jczzq/mone...
Demo: https://blog.jczzq.com/mone-q...

mone-query是基于element-ui封装的通用查询组件,它通过丰富的配置让你尽可能少的前端编码就可以完成大部分报表需求。

必要依赖

vue >= 2.5.2

element-ui >= 2.4.0

axios >= 0.16.0

安装

CommonJS方式

npm install mone-query -D

umd方式

<link href="mone-query/lib/style.css" rel="stylesheet">
<script data-original="../mone-query/lib/index.js"></script>

快速上手

import Vue from "vue";
import 'mone-query/style.css';
import MoneQuery from "mone-query";
Vue.use(MoneQuery, {
  // options
  ...
})
<mone-query border :config="config" :data="data" />
...
import Config from "@/Config.json";
import Data from "@/Data.json";
data() {
  return {
    // 传入请求路径
    config: "/api/config",
    data: "/api/data"
    // or
    // 传入对象
    config: Config.resultData,
    data: Data.resultData
  };
}

运行机制

  1. 校验入参

    mone-query有两个必要参数configdata, config控制表格结构和自定义功能,data渲染数据行,这两个参数可以传入ajax路径字符串,也可以传入json对象,其他类型的参数传入时会抛出异常。注意组件还有其它一些参数可以传入,组件传入的参数与config有一些参数是重复的,甚至你还可以在Vue.use时给所有的组件实例传入默认参数,这是为了增加组件配置的灵活性,不过通过组件直接传入的参数优先级最高,config属性其次,最后是默认配置

  2. 根据config参数获取配置

    如果config传入字符串,那么组件首先会发起Get请求查询配置json,这段期间表格会一直loading到请求结束,如果请求失败,表格将会触发on-error事件,使用者可以监听事件做后续处理,或者让页面保持错误面板的状态;请求成功的结果将直接作为配置对象使用。如果config传入json,将直接作为配置对象使用(具体结构示例参照Config.json)。

  3. 根据配置对象渲染表结构

    受配置影响的部分包括:

    • colbox: 工具栏的字段面板
    • cols: 表头
    • ......
  4. 根据data参数获取结果集

    如果data传入字符串,那么组件会将之作为ajax路径查询结果集。如果data传入json,将直接作为结果集使用(具体结构示例参照Data.json)。

  5. 有时候在结果集渲染之前需要对某些字段进行处理,比如映射新值、时间转换等等,这时提供formatters对象,这是一个属性名为col.prop、属性值为该列过滤函数的对象,过滤函数有四个形参:row, column, cellValue, index

MoneQuery Attributes

属性名类型含义可选值默认值
*configObject , String表头列ajax路径配置对象{}
*dataArray , String数据行ajax路径数据集合[]
heightString/NumberTable高度auto8080pxauto
max-heightString/NumberTable的最大高度--
borderBoolean是否带有纵向边框-false
primary-keyString主键(数据行多选时必要)-id
page-nameString分页参数中的pageSize键名-pageIndex
size-nameString分页参数中的pageSize键名-pageSize
rows-nameStringajax请求结果集键名-resultData.rows
total-nameStringajax请求结果总数键名-resultData.total
colboxObject字段显示框方位-{ placement: "top", width: "540px", trigger: "click" }
visible-fieldsBoolean,Array默认显示字段true: 全部显示; false: 全部隐藏; ["propA", "propB", ...]true
visible-fields-configArray<FieldGroup>字段在工具栏的显示配置--
formattersObject包含各个列过滤函数的对象-{}
show-actionBoolean是否显示右侧操作栏-false
show-deleteBoolean是否显示删除按钮-false
show-headerBoolean是否显示表头(包含筛选条件)-true
show-selectionBoolean是否显示多选框-true
show-indexBoolean是否自定义序列-false

MoneQuery Events

事件名说明参数
delete点击删除按钮触发selection
search点击查询按钮触发parameters
config-success查询配置成功res
config-error查询配置失败error
config-complete查询配置完成(成功或失败都会触发)-
data-success查询结果集成功res
data-error查询结果集失败error
data-complete查询结果集完成(成功或失败都会触发)-

config Attributes

属性名类型含义可选值默认值
baseUrlStringajax根路径--
primary-keyString主键(数据行多选时必要)-id
colsArray<Col>-id
page-nameString分页参数中的pageSize键名-pageIndex
size-nameString分页参数中的pageSize键名-pageSize
rows-nameStringajax请求结果集键名-resultData.rows
total-nameStringajax请求结果总数键名-resultData.total
colboxObject字段显示框方位-{ placement: "top", width: "540px", trigger: "click" }
visible-fieldsBoolean,Array默认显示字段true: 全部显示; false: 全部隐藏; ["propA", "propB", ...]true
visible-fields-configArray<FieldGroup>字段在工具栏的显示配置--
show-actionBoolean是否显示右侧操作栏-false
show-deleteBoolean是否显示删除按钮-false
show-headerBoolean是否显示表头(包含筛选条件)-true
show-selectionBoolean是否显示多选框-true
show-indexBoolean是否自定义序列-false

colbox Attributes

属性名类型含义可选值默认值
placementString展示方位-top
widthString面板宽度-540px
triggerString触发事件类型-click

Col class

属性名类型含义可选值默认值
labelString列标题--
propString列字段名--
typeString列字段类型varcharoptiondatedatetime-
widthString列宽度widthwidth
orderNumber排列顺序--
actionString查询类型-varchar:lk,option:in; data:lt& gt; datetime:le& ge
placeholderString输入提示--
fixedBoolean是否固定列--
optionsArray多选组件的选项列表,eg: [{label, value}]--

Col.type enum

枚举值含义
varchar文本
option多选
date日期
datetime日期时间

FieldGroup class

属性名类型含义可选值默认值
titleString组标题--
orderString排列顺序--
selectionArray选中的--
checkAllBoolean是否全选-true
isIndeterminateBoolean是否半选-false
colPropsArray组成员--

查询配置标准结构(application/json)

  • resultCode 响应码
  • resultData 响应结果(注意resultData必须是JSON对象)

    • primaryKey String 主键prop(默认:id)
    • rowsName String 结果集属性名,将根据rowsName指定的属性名获取目标结果集(默认: resultData.rows)
    • totalName String 结果总数属性名,将根据totalName指定的属性名获取目标结果总数(默认: resultData.total)
    • pageName String 当前页属性名,将根据pageName指定的属性名设置分页参数(默认: pageIndex)
    • sizeName String 每页条数属性名,将根据sizeName指定的属性名设置分页参数(默认: pageSize)
    • pageSize Number 默认每页查询的数据数量(默认: 20)
    • cols Array 列头(字段)对象集合

      • label String 标题
      • prop String 属性名
      • type String 属性类型

        • varchar 文本
        • option 多选
        • date 日期
        • datetime 日期时间
        • bit 单选(暂不支持)
        • int 数字(暂不支持)
        • time 时间(暂不支持)
      • width String 列宽
      • order Number 排列顺序
    • colbox Object 可选字段配置

      • placement String 展示方位(默认:top)
      • width String 面板宽度(默认:540px)
      • trigger String 触发事件类型(默认:click)
      • value Array (默认:所有字段)

查询参数标准结构(application/json)

  • colProps Array 要查询的字段列
  • params Array 查询参数字段

    • field String
    • action String

      • eq 等于
      • ne 不等于
      • in 包含在
      • ni 不包含在
      • sw start with
      • ew end with
      • lk like
      • gt 大于
      • lt 小于
      • ge 大于等于
      • le 小于等于
    • filedType String

      • varchar 字符串
      • option 多选
      • date 日期范围
      • datetime 日期时间范围
      • bit 单选(暂不支持)
      • int 数字(暂不支持)
      • time 时间(暂不支持)
    • value any
  • sort Array 排序字段

    • prop String 字段名
    • type String 排序方式

      • asc 正序
      • desc 降序
  • page Object

    • pageIndex Number 当前页(默认:1)
    • pageSize Number 每页多少条(默认:20)
# 查询参数示例
cols: ["name", "age", "grade", "isMarried", "birthday", "schoolTime"],
params: [
  {
    field: "name",
    action: "lk",
    filedType: "varchar",
    value: "李",
  },
  {
    field: "birthday",
    action: "lt",
    filedType: "datetime",
    value: "2019-04-13",
  },
  {
    field: "birthday",
    action: "ge",
    filedType: "datetime",
    value: "2019-03-13",
  },
  {
    field: "grade",
    action: "in",
    filedType: "option",
    value: [1,2,5],
  }
]
sort: [
  {
    prop: "age',
    type: "asc"
  }
],
page: {
  pageIndex: 1,
  pageSize: 20
}

查询结果集标准结构(application/json)

  • [resultCode] 响应码
  • [resultData] 响应结果

    • [rows] 结果集
    • [total] 结果总数

轻量版

mone-query默认依赖element-ui部分组件和axios请求库,这里有两个构建版供你的应用选择:

  • 全部引入(js≈360kb, style≈103kb):包括element相关组件(可以在应用中使用这些组件,因为它们已经全局注册过了);另外是axios。
  • 只引入核心代码(≈24kb, style≈1kb),如果你的应用已经引入了element和axios,那建议只引入核心代码即可。

      import "mone-query/lib/lite/style.css";
      import moneQuery from "mone-query/lib/lite";
      ...

PS: 这里说来说去最终不影响物质守恒,如果你不是太关心你应用的构建体积可以忽略

查看原文

赞 0 收藏 0 评论 0

jczzq 关注了标签 · 2019-03-27

element-ui

Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库

A Vue.js 2.0 UI Toolkit for Web

https://element.eleme.cn/
https://github.com/ElemeFE/el...

关注 660

jczzq 关注了标签 · 2018-10-29

css3

层叠样式表(英语:Cascading Style Sheets,简写CSS),又称串样式列表,由W3C定义和维护的标准,一种用来为结构化文档(如HTML文档或XML应用)添加样式(字体、间距和颜色等)的计算机语言。目前最新版本是CSS2.1,为W3C的候选推荐标准。CSS3现在已被大部分现代浏览器支持,而下一版的CSS4仍在开发过程中。

关注 23322

jczzq 关注了标签 · 2018-10-29

linux

Linux是一种自由和开放源代码的类Unix计算机操作系统。目前存在着许多不同的Linux,但它们全都使用了Linux内核。Linux可安装在各种各样的计算机硬件设备,从手机、平板电脑、路由器和视频游戏控制台,到台式计算机,大型机和超级计算机。

Linux家族家谱图,很全很强大!! 图中可以清楚的看出各个Linux发行版的血缘关系。无水印原图:http://url.cn/5ONhQb

关注 78574

jczzq 关注了标签 · 2018-10-29

前端

Web前端开发是从网页制作演变而来的,名称上有很明显的时代特征。在互联网的演化进程中,网页制作是Web 1.0时代的产物,那时网站的主要内容都是静态的,用户使用网站的行为也以浏览为主。2005年以后,互联网进入Web 2.0时代,各种类似桌面软件的Web应用大量涌现,网站的前端由此发生了翻天覆地的变化。网页不再只是承载单一的文字和图片,各种富媒体让网页的内容更加生动,网页上软件化的交互形式为用户提供了更好的使用体验,这些都是基于前端技术实现的。

Web前端优化
  1. 尽量减少HTTP请求 (Make Fewer HTTP Requests)
  2. 减少 DNS 查找 (Reduce DNS Lookups)
  3. 避免重定向 (Avoid Redirects)
  4. 使得 Ajax 可缓存 (Make Ajax Cacheable)
  5. 延迟载入组件 (Post-load Components)
  6. 预载入组件 (Preload Components)
  7. 减少 DOM 元素数量 (Reduce the Number of DOM Elements)
  8. 切分组件到多个域 (Split Components Across Domains)
  9. 最小化 iframe 的数量 (Minimize the Number of iframes)
  10. 杜绝 http 404 错误 (No 404s)

关注 190567

认证与成就

  • 获得 7 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-07-25
个人主页被 568 人浏览