专栏首页changyandouVue.js 通过举一反三建立企业级组件库

Vue.js 通过举一反三建立企业级组件库

  • 如何安装插件
  • 如何从开源插件的源码获得经验
  • 解耦的关键点是什么?
  • 如何灵活控制复杂样式
  • 建立企业级内部组件库的详细步骤
  • Bootstrap 的 alert
  • bootstrapVue 的 alert
  • view-design 中的 alert
  • 安装 npm
  • 安装 verdaccio
  • 更新 node
  • node 更新后正常运行
  • 添加用户
  • pm2
  • 包如何管理,如何使用,关键是制作、发布
  • Windows 安装

不同的单页面应用中的标签存在大幅度的重复,这个时候我们会很快想到使用组件,但用法各式各样,逻辑混乱复杂。这个时候会立即想到解耦?解耦完后如何在公司内部建立组件库供其他人使用?

  • 如何安装插件
  • 如何从开源插件的源码获得经验
  • 解耦的关键点是什么
  • 如何灵活控制复杂样式
  • 建立企业级内部组件库的详细步骤

如何安装插件

在 Vue 的插件的使用过程中,首先需要搞清楚几个概念,如下:

(图片来自:https://cn.vuejs.org)

按照惯例,遇到问题,首先去官方的说明或者提供的文档中寻找最初的答案。上图中就对插件进行了进行了概括的介绍。

从这个说明中能够知道,插件通常为 Vue 提供全局功能。

在我们通常的工作中,如果引入通用的第三方插件,比如 elementUI、view-design、jQuery 等等,均可以在入口 JS 使用 Vue.use 来调用插件。

在谈到 Vue.use 的时候,通过了解关于 Vue.use 的基本信息,如下:

(图片来自:https://cn.vuejs.org)

这里就提到了两点重要的信息:

  1. 如果插件是一个对象,必须提供 install 方法。
  2. 如果插件是一个函数,它会被作为 install 方法。

对于这两点说明,我们在后续的 Bootstrap、bootstrapVue 和 view-design 三个第三方插件的 alert 的说明中可以看到它们对于 install 方法的不同的使用过程。

对于 install 方法的简要说明如下:

(图片来自:https://cn.vuejs.org)

如何从开源插件的源码获得经验

在文章《深入解读 iView,解耦令人头疼的高度耦合负责逻辑》中笔者重点分析了 view-design 中的 Tree 组件是如何一步步完成的。这里我们沿用这种从源码中汲取经验的方法。

开放成熟的源码,往往在算法、业务、编码规范等等不同的角度,有比较高的参考价值。

在本文中,我们不仅仅局限于 view-design,结合 Bootstrap 和 bootstrapVue 来从横纵不同的方向,通过对比深入理解插件的实现过程。

Bootstrap 的 alert

首先参考官方的说明,来对基本环境进行配置:

(图片来自:https://v4.bootcss.com)

(图片来自:https://v4.bootcss.com)

配置完成 babel-loader 的加载器,在模板中调用,可以通过调试,查看执行过程:

下面是 Bootstrap 插件对应 alert 模块的的实现步骤:

/**
 * --------------------------------------------------------------------------
 * Bootstrap (v4.5.0): alert.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

import $ from 'jquery'
import Util from './util'

/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */

const NAME                = 'alert'
const VERSION             = '4.5.0'
const DATA_KEY            = 'bs.alert'
const EVENT_KEY           = `.${DATA_KEY}`
const DATA_API_KEY        = '.data-api'
const JQUERY_NO_CONFLICT  = $.fn[NAME]

const SELECTOR_DISMISS = '[data-dismiss="alert"]'

const EVENT_CLOSE          = `close${EVENT_KEY}`
const EVENT_CLOSED         = `closed${EVENT_KEY}`

/**
 * click.bs.alert.data-api
 */
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`



const CLASS_NAME_ALERT = 'alert'
const CLASS_NAME_FADE  = 'fade'
const CLASS_NAME_SHOW  = 'show'

/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */

class Alert {

/**
 * 构造函数的使用方法和用途:
 *    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor
 *  
 */

  constructor(element) {
    debugger;
    this._element = element
  }

  // Getters

  /**
   * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/static
   * get 语法将对象属性绑定到查询该属性时将被调用的函数。
   * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/get
   */
  static get VERSION() {
    debugger;
    return VERSION
  }

  // Public
  close(element) {
    debugger;
    let rootElement = this._element
    if (element) {
      rootElement = this._getRootElement(element)
    }

    const customEvent = this._triggerCloseEvent(rootElement)

    if (customEvent.isDefaultPrevented()) {
      return
    }

    this._removeElement(rootElement)
  }

  dispose() {
    debugger;
    $.removeData(this._element, DATA_KEY)
    this._element = null
  }

  // Private

  _getRootElement(element) {
    debugger;
    const selector = Util.getSelectorFromElement(element)
    let parent     = false

    if (selector) {
      parent = document.querySelector(selector)
    }

    if (!parent) {
      parent = $(element).closest(`.${CLASS_NAME_ALERT}`)[0]
    }

    return parent
  }

  _triggerCloseEvent(element) {
    debugger;
    const closeEvent = $.Event(EVENT_CLOSE)

    $(element).trigger(closeEvent)
    return closeEvent
  }

  _removeElement(element) {
    debugger;
    $(element).removeClass(CLASS_NAME_SHOW)

    if (!$(element).hasClass(CLASS_NAME_FADE)) {
      this._destroyElement(element)
      return
    }

    const transitionDuration = Util.getTransitionDurationFromElement(element)

    $(element)
      .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
      .emulateTransitionEnd(transitionDuration)
  }

  _destroyElement(element) {
    debugger;
    $(element)
      .detach()
      .trigger(EVENT_CLOSED)
      .remove()
  }

  // Static

  static _jQueryInterface(config) {
    debugger;
    return this.each(function () {
      const $element = $(this)
      let data       = $element.data(DATA_KEY)

      if (!data) {
        data = new Alert(this)
        $element.data(DATA_KEY, data)
      }

      if (config === 'close') {
        data[config](this)
      }
    })
  }

  static _handleDismiss(alertInstance) {
    debugger;
    return function (event) {
      if (event) {
        event.preventDefault()
      }

      alertInstance.close(this)
    }
  }
}

/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */

/**
 * EVENT_CLICK_DATA_API=>click.bs.alert.data-api,事件
 * SELECTOR_DISMISS=>[data-dismiss="alert"],子选择器
 * 
 * on() 方法在被选元素及其子元素上添加一个或多个事件处理程序
 * 
 * 语法:$(selector).on(event,childSelector,data,function)
 * 
 * event:一个或多个用空格分隔的事件类型和可选的命名空间
 * childSelector:规定只能添加到指定的子元素上的事件处理程序
 * function:当事件发生时要运行的函数
 */

$(document).on(
  EVENT_CLICK_DATA_API,
  SELECTOR_DISMISS,
  Alert._handleDismiss(new Alert())
)

/**
 * ------------------------------------------------------------------------
 * jQuery
 * ------------------------------------------------------------------------
 */

$.fn[NAME]             = Alert._jQueryInterface
$.fn[NAME].Constructor = Alert
$.fn[NAME].noConflict  = () => {
  debugger;
  $.fn[NAME] = JQUERY_NO_CONFLICT
  return Alert._jQueryInterface
}

export default Alert

这种方案在我们工作中实际上是比较常见的,但是它也是相对比较简洁的一种方式,没有过于复杂的逻辑。

通过 .fn[NAME] 的形式,也就是使用属性访问器,来对 jQuery 的原型进行扩展,然后再直接使用 (selector).alert('...') 进行调用。

其余的地方也都比较容易理解,通过不同的方法,对于元素的处理和事件的处置,均分离开来,这种情况下,也就相当于我们在设计模式中常常提到的单一职责的原则。

在这种方式下,才能够使不同的方法,不同的对象满足不同的功能。在单一职责的粒度相对较细的情况下,也便于划分功能界限,易于维护和修改。

举例来说,我们工作中常常用的分页组件,就可以把首页、末页、上一页、下一页、跳转到指定页,不同的动作进行拆分。但是这些不同的事件中有一个相同的动作就是翻页的动作,也就是说不论使用哪一个事件,它都将触发翻页。那么翻页的逻辑就可以作为公共业务提取。

这样我们常常感到头疼的翻页功能,便能够轻易的解决,并且预留对外的翻页后发生的事件监听接口,在翻页的过程中实现自定义的事件操作即可。而这个监听的过程实际上就是我们一些面向对象的语言中用到的观察者模式,比如 click 事件。

bootstrapVue 的 alert

(图片来自:https://bootstrap-vue.org)

对于 bootstrapVue 来说,它和 Bootstrap 的区别还是比较大的。它的组件的提取方式上就和 Bootstrap 有不小的差别。Bootstrap 的 Dom 的基本内容基本上向原有的 Bootstrap 一样,大部分 dom 元素是直接写在外部的。

可是对于 bootstrapVue 来说,它作为组件的内容给提取到组件内部了。

导出组件的方式决定引入组件的方法,那么我们来看导出组件的方式是什么样的:

然后,我们观察一下 bootstrapVue:

注意这里的插件工厂的实现过程,它就与笔者在文章开始提到的,如果插件时一个对象,就必须提供一个 install 方法进行了实践。

通过公共的工厂,实现对应组件的注册安装。

前面说的都是组件的导出和注册使用上的区别,然后再深入组件内部,我们可以发现,这又是一种比较特殊的方式,它 iView 中的 Tree 组件不同的是,虽然它没有对应的后缀名为.vue 的模板文件,但是它的业务和渲染的过程完成在了 alert.js 中,这不仅和 iview 有所区别,它和 Bootstrap 也有区别,如下:

包括 NAME、参数、方法等等的定义,以及渲染方法的使用。

view-design 中的 alert

对于 view-design,在上一篇文章中我们已经做了详细介绍了在 Tree 组件的整个生命周期过程中涉及到的关键问题。这里为了用 alert 做横向的对比,我们又提到了它。可以看出它能够折射出 Tree 组件的影子。

这里值得我们借鉴的是,它使用 template 模板,而不是 render 函数来渲染 Dom。这种方式对于开发者来说打包之前的工作控制器里会稍微便捷,直白一点。

另外,它对于前缀的定义和使用,起到了命名空间的作用,这种情况下对于全局样式不会造成干扰。能够有效的从其他的类似元素中区分开来,独立控制。如下图:

对于一些其他的全局变量的提取,可以参考 index.js 中的 Vue.prototype.$IVIEW

通过这种横向的对比,以及就 view-design 而言,通过 alert 与上一篇中的 Tree 组件的纵向的对比,我们大概能够看出来对于组件的提取的常用方法。

然后,在结合我们自己创建的内部组件库,可以通过逐渐的扩展建立企业内部的私有组件库。减少重复工作量,节约开发成本。

解耦的关键点是什么?

解耦的关键点在于对于业务模块的结构要足够清晰。我们可以针对自己已经完成过的业务模块进行回顾。比如在线购物平台中最常见的购物车,比如 CMS 中的图文记录,比如涉及到年份、温度时候的 range slider,比如我们所有平台都离不开的翻页,等等。

这些功能点,基本上我们多多少少都会接触过那么几个。那么我们已经实现的功能,当产品经理带着需求走过来的时候,我们是否能够直接复用,还是再摘抄一遍代码后,重新调试,重新测试呢?

现实可能是,我们需要去一遍一遍的复制粘贴自己的代码或者是别人的代码。那么,如何改变这个现状,不再被产品部门的需求牵着鼻子往前走呢?解耦。

通过业务解耦,把公共业务分割,提取公用。像用第三方插件一样,去通过解耦,建立属于自己,属于团队,属于公司内部的组件库。

依托于解耦的代码,来实现业务的解耦。这个时候,我们在熟练掌握公司业务的基础上,需要去“23 种设计模式,6 大设计原则”中寻找答案。

明确观察者模式、策略模式、工厂模式、状态模式,等着这些常用模式对于常用业务的处理过程。遵守设计原则,比如在前面提到的几个插件中,都能够看出来它们对于不同功能模块的分割,各自的业务之间几乎没有任何交叉。这就是我们常说的“单一职责”原则,谁的活谁自己干,互不打扰,但是又能够协同作战。

如何灵活控制复杂样式

这个问题,在 iView 中,也就是 view-design 的源码中可以得到经验,再次使用前面的一张图:

  • 通过使用 prefixCls 这种前缀,相当于为元素使用命名空间,来区分其他的元素,避免引起冲突
  • 在拼接的类中,通过 computed 计算属性或者是 watch 侦听属性,来实现样式的动态修改。
  • 对于使用添加前缀区分后的 class 属性,通过使用外部的独立 css 文件中对于样式进行动态控制。

应该尽量避免使用内联的样式,内联样式在维护起来的时候相对麻烦。

建立企业级内部组件库的详细步骤

在日常工作中,不管是前端也好,后端也好,亦或是移动端也好。基本上都离不开以下的几种内容:

  • 列表,它包括图文或者纯文本,图文中可能又包括左图右文、左文右图、上文下图等等方法。
  • 懒加载,涉及到列表的地方,往往我们会看到对应的懒加载的业务。
  • 分页,在我们几乎所有的涉及到内容,或者大于 20 条数据的结果集的时候,几乎都会涉及到分页的问题。
  • 选项卡。
  • 树形图。

等许许多多的内容,可能都是我们的常规工作中经常会遇到的一些实现方法和方式。那么当我们在不同的项目中一次又一次的拷贝相同的内容的时候,我们不妨反思一下,这么做是否有意义,我们的时间是否应该浪费在这个问题上,如何寻找对应的出路?

针对 Vue 有没有像 Java 中对于 jar 包使用 Maven 或者 Gradle 管理的类似工具。这种情况下,我们应该如何搭建一套属于公司内部的 npm 呢?

下面来看一下,Verdacciod 的使用方法。

认识 Verdacciod:

这个东西,怎么安装,怎么使用,这里涉及到了持续交付的问题。尽可能地从官方提供的信息中获取官方的权威消息。

  • 中文:https://verdaccio.org/docs/zh-CN/what-is-verdaccio
  • 英文:https://verdaccio.org/docs/en/what-is-verdaccio

Windows 安装

执行命令,全局安装 verdaccio:

npm install -g verdaccio

执行 verdaccio 得到如下结果:

文本内容如下(ME:注意删掉不必要的信息,和遮盖图中的敏感信息):

PS G:\*******> verdaccio
 warn --- config file  - C:\Users\******\AppData\Roaming\verdaccio\config.yaml
 warn --- Verdaccio started
 warn --- Plugin successfully loaded: verdaccio-htpasswd
 warn --- Plugin successfully loaded: verdaccio-audit
 warn --- http address - http://localhost:4873/ - verdaccio/4.6.2

查看 config.yaml 配置文件,内容如下:

#
# This is the default config file. It allows all users to do anything,
# so don't use it on production systems.
#
# Look here for more config file examples:
# https://github.com/verdaccio/verdaccio/tree/master/conf
#

# path to a directory with all packages
storage: ./storage
# path to a directory with plugins to include
plugins: ./plugins

web:
  title: Verdaccio
  # comment out to disable gravatar support 取消注释禁用 gravatar
  # gravatar: false
  # by default packages are ordercer ascendant (asc|desc)
  # sort_packages: asc
  # convert your UI to the dark side
  # darkMode: true

# translate your registry, api i18n not available yet
# i18n:
# list of the available translations https://github.com/verdaccio/ui/tree/master/i18n/translations
#   web: en-US

auth:
  htpasswd:
    file: ./htpasswd
    # Maximum amount of users allowed to register, defaults to "+inf".
    # You can set this to -1 to disable registration.
    # max_users: 1000

# a list of other known repositories we can talk to
uplinks:
  npmjs:
    url: https://registry.npmjs.org/

packages:
  '@*/*':
    # scoped packages
    access: $all
    publish: $authenticated
    unpublish: $authenticated
    proxy: npmjs

  '**':
    # allow all users (including non-authenticated users) to read and
    # publish all packages
    #
    # you can specify usernames/groupnames (depending on your auth plugin)
    # and three keywords: "$all", "$anonymous", "$authenticated"
    access: $all

    # allow all known users to publish/publish packages
    # (anyone can register by default, remember?)
    publish: $authenticated
    unpublish: $authenticated

    # if package is not available locally, proxy requests to 'npmjs' registry
    proxy: npmjs

# You can specify HTTP/1.1 server keep alive timeout in seconds for incoming connections.
# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout.
# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enough.
server:
  keepAliveTimeout: 60

middlewares:
  audit:
    enabled: true

# log settings
logs:
  - { type: stdout, format: pretty, level: http }
  #- {type: file, path: verdaccio.log, level: info}
#experiments:
#  # support for npm token command
#  token: false

# This affect the web and api (not developed yet)
#i18n:
#web: en-US

使用 localhost:4783 在浏览器中访问,得到如下内容:

可以选择语种为你熟悉的或者习惯使用的语种:

修改后语种得到修改:

如果使用 VS Code 的情况下,可以新建一个终端窗口:

根据需要,进行选择切换。在新建的窗口中输入 上述步骤中 localhost:4873 对应的 verdaccio 首页中添加用户的提示:

拷贝执行,查看得到的执行结果:

根据提示,输入并执行,设置:

  • Username:changyandou
  • Password:changyandou(注:密码在终端中输入的时候不会显现出来)
  • Email:changyandou@126.com

最后打印输出内容:

Logged in as changyandou on http://localhost:4873

然后按照第二步骤提示的发布来操作:

npm publish --registry http://localhost:4873

得到如下的提示内容:

npm notice 
npm notice package: ***@1.0.0
npm notice === Tarball Contents === 
npm notice 242B  .babelrc
npm notice 156B  .editorconfig
npm notice 0     static/.gitkeep
npm notice 6.4kB src/css/iconfont.css       
npm notice 1.1kB src/css/reset.css
npm notice 277B  index.html
npm notice 256B  .postcssrc.js
npm notice 1.2kB build/build.js
npm notice 1.3kB build/check-versions.js    
npm notice 163B  config/dev.env.js
npm notice 2.0kB config/index.js
npm notice 879B  src/router/index.js        
npm notice 482B  src/main.js
npm notice 65B   config/prod.env.js
npm notice 2.7kB build/utils.js
npm notice 575B  build/vue-loader.conf.js
npm notice 2.2kB build/webpack.base.conf.js 
npm notice 3.1kB build/webpack.dev.conf.js
npm notice 5.2kB build/webpack.prod.conf.js
npm notice 1.9kB package.json
npm notice 481B  README.md
npm notice 6.8kB build/logo.png
npm notice 6.8kB src/assets/logo.png
npm notice 343B  src/App.vue
npm notice 4.2kB src/components/***.vue
npm notice 604B  src/components/***.vue
npm notice 269B  src/components/***.vue
npm notice 1.2kB src/components/***.vue
npm notice 631B  src/components/***.vue
npm notice 633B  src/components/***.vue
npm notice 820B  src/components/***.vue
npm notice === Tarball Details ===
npm notice name:          ***
npm notice version:       1.0.0
npm notice package size:  19.7 kB
npm notice unpacked size: 53.2 kB
npm notice shasum:        c81194aec18b6d2e072813b62e2886265a3163f4
npm notice integrity:     sha512-gPlPMEYM2i4l3[...]dvT6BDBVyPYDQ==
npm notice total files:   31
npm notice
npm ERR! code EPRIVATE
npm ERR! This package has been marked as private
npm ERR! Remove the 'private' field from the package.json to publish it.

npm ERR! A complete log of this run can be found in:
npm ERR!     D:\nodejs\node_cache\_logs\2020-05-20T15_24_24_490Z-debug.log

从这儿能够看到发布失败。根据错误提示可知道,当前包已经被标记为 private,从 package.json 中移除 private 字段才能够发布。

Linux 安装

安装 npm
yum install npm
安装 verdaccio
npm install verdaccio
npm install -g verdaccio
verdaccio

启动报错,原因如下:

(图片来自:https://verdaccio.org)

更新 node
npm install -g n
n lts

输出环境变量,查看新的路径是否定义在 PATH 的路径下:

echo $PATH 
node 更新后正常运行

使用 IP 依然连不上,查看端口状态:

cd /root/.config/verdaccio/
vim config.yaml

修改完后再次启动,对比查看:

再次使用公网 IP 访问,内网的可以使用局域网 IP 访问:

添加用户

按照首页说明,拷贝命令,执行,添加用户:

npm adduser --registry http://***.***.***.***:4873

登录:

如果从公司回家办公,依然想要使用公司添加的用户信息,如何操作?直接安装发布包肯定是不现实的,因为家里的电脑没有连接配置的 verdaccio,如何操作呢?

npm set registry="http://***.***.***.***:4873"
npm login

输入公司设置的账号信息后回车,可以看到对应的提示,logged in as chyd on http://***:4873 ,也就是说在这种情况下,登录成功。

pm2

守护进程

npm install -g pm2 --unsafe-perm
pm2 start `which verdaccio`
包如何管理,如何使用,关键是制作、发布
pm2 start verdaccio

安装 nrm

npm install -g nrm

(注意:处理敏感信息)

nrm ls

制作包:创建一个文件夹。

假设创建一个输入框,带有回车事件的输入框,进入 input 的目录:

cd pkg/input
npm init

这个时候能看到 pkg/input 文件夹下多了一个 package.json 的文件。

配置自己配置的 verdaccio:

npm set registry http://***:4873
npm publish

提示错误:

添加用户:

npm adduser --registry http://***:4873

再次发布:

可以看到:

修改后再发布:

npm ERR! code EPUBLISHCONFLICT
npm ERR! publish fail Cannot publish over existing version.
npm ERR! publish fail Update the 'version' field in package.json and try again.
npm ERR! publish fail
npm ERR! publish fail To automatically increment version numbers, see:
npm ERR! publish fail     npm help version
npm unpublish inputenter --force

刷新刚才看到的发布页面,已经找不到 inputenter 的包了。

再次发布:

可以看到添加的 README.md 对应于 README 上的显示。

发布完成后尝试安装:

npm install inputenter

查看当前的注册列表:

nrm ls

nrm add cydnpm http://***:4873

回到 project 的根目录,执行安装命令:

在 node_modules 下可以看到对应的包文件:

随后,如果有机会将就开发、生产、测试等不同环境下基于 webpack 的构建方法再次进行讨论。欢迎交流、指正。

本文分享自微信公众号 - changyandou(changyandou),作者:长烟斗火枪红帽子鹿皮鞋

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-04-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue、React 和 Angular:该选择哪个框架?

    本文对三个最流行的 JavaScript 框架进行了全面的比较:Vue、React 和 Angular,如果你是正在开发或者目前正在考虑使用这些流行框架之一来...

    深度学习与Python
  • 为什么现在的开发者总是拿 Vue.js 和 JavaScript 巨头 Angular、React 比较?

    Vue.js 是一个用来构建 web UI 的 JavaScript 库,基于 MIT 开源协议。Vue.js 于 2013 年首次分布,但是在接下来的两年内,...

    疯狂的技术宅
  • Vue PC端框架

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

    grain先森
  • 围观!码云5月新增GVP(最有价值)和 1000Star 项目!

    码云Gitee
  • 盘点那些走向世界的中国项目

    Kylin™是第一个成为Apache顶级开源项目的中国作品。它是一个分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模...

    IT大咖说
  • 【前端架构】Angular,React,Vue哪个是2021的最佳选择

    当开始一个新的web开发项目时,许多开发人员都有一个问题:“什么工具是最合适的?”

    首席架构师智库
  • 如何选择一个 vue ui 框架?

    “Material Design 并不是一种单一风格,而代表着一套源自纸张与墨水的适应性设计系统。经过精心编排,你将能够更快构建起美观且实用的产品。”

    程序员LIYI
  • 初学前端需要怎么学习?

    给你一条学习路线,初学前端的话,需要先学习一下前端的三大基础知识,即HTML、CSS和JavaScript 。

    用户8671053
  • 01 . Vue简介,原理,环境安装及简单hello案例

    对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:

    常见_youmen
  • 腾讯工程师们怎么玩 Vue.js?

    Vue.js 是国人尤雨溪开发的一套前端框架。从去年开始,就火遍了国内外。本文精选了腾云阁中分享的相关优质技术文章,从入门、实战、源码、优化等多个方面进行了阐述...

    云加社区
  • Vue管理后台框架选择推荐

    Element UI 是一套采用 Vue 2.0 作为基础框架实现的组件库,它面向企业级的后台应用,能够帮助你快速地搭建网站,极大地减少研发的人力与时间成本。在...

    用户5807183
  • 8分钟为你详解React、Angular、Vue三大框架

    当前世界中,技术发展非常迅速并且变化迅速,开发者需要更多的开发工具来解决不同的问题。本文就对于当下主流的前端开发技术React、Vue、Angular这三个框架...

    极乐君
  • 2019年度最受欢迎前端开源技术,你会多少?

    就在今天,开源中国最新统计的2019年最受开发者欢迎的开源软件排名出来了,关于前端部分,vue,react,小程序依旧是大家的心中所爱,技术更新换代太快,各种框...

    王小婷
  • 推荐6款Vue管理后台框架,收藏好,留备用

    来源 | https://www.jianshu.com/p/0f41bfe211a8

    ConardLi
  • Angular 中后台前端解决方案 - Ng Alain 介绍

    JadePeng
  • 新型前端开发工程师的三个境界 后端开发工程师如何快速转前端

    初入软件开发这一行时,当时还没有前后端分离这个概念,所有的开发工程师既能写html,也能写后台服务,随着技术的发展,前后端分离成为趋势,目前团队不少人能熟悉的写...

    JadePeng
  • 2018年九个很受欢迎的vue前端UI框架

    最近在逛各大网站,论坛,SegmentFault等编程问答社区,发现Vue.js异常火爆,重复性的提问和内容也很多,小编自己也趁着这个大前端的热潮,着手学习了一...

    王小婷
  • 【拓展】700- MVVM模式理解

    Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于View 层。它的核心是 MVVM 中的 VM,也就是 ViewMo...

    pingan8787
  • Vue.js的组件、组件间通信

    props定义了这个组件有哪些可配置的属性,props最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值。

    用户3258338

扫码关注云+社区

领取腾讯云代金券