浅谈vue 单文件探索


Posted in Javascript onSeptember 05, 2018

在很多Vue项目中,我们使用 Vue.component 来定义全局组件,紧接着用new Vue({ el: '#container '}) 在每个页面内指定一个容器元素。

这种方案在只是使用 JavaScript 增强某个视图的中小型项目中表现得很好。然而在更复杂的项目中,或者当你的前端完全采用 JavaScript 驱动的时候,以下弊端就显现出来:

  • 全局定义(Global definitions) 强制要求每个 component 中的命名不得重复
  • 字符串模板(String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的\
  • 不支持CSS(No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤(No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

文件扩展名为 .vue 的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 Webpack 或 Browserify 等构建工具。

以 vue 作为开发技术栈的前端开发者,往往会配合前端构建工具,进行项目的工程化管理。比如,大家常用的 vue 全家桶 + webpack 的方案进行一些中大型前端项目的开发。配合 webpack 后,vue 的组件化优势更加明显,我们可以通过单文件的组件化开发方式,在工作实践中搭建前端页面,从而提高开发效率。 有这样一个问题:“当我们在写 vue 单文件时,我们在写什么?” 很多人可能会这样回答:template 负责模板,javascript 负责逻辑,style 负责样式。当回答到这里时,一个 vue 开发者的世界观基本上算是很明确了。我们要做的就是在一个单文件组件中写 template、javascript、style。如果仅仅局限于此,显然我们无法从更好的利用的单文件组件服务我们的整个开发流程。接下来我将和大家讨论在 vue 单文件开发中的一些方法论的问题。

vue 单文件本质

vue单文件是以特定文件扩展名 .vue 命名的文件。如下所示的代码:

ListDemo.vue

<template>
  <div class="list-demo">
    <ul>
      <li v-for="item in list" :key="item.key">{{item.value}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'ListNav',
  data() {
    return {
      list: [
        { key: 'home', value: '首页' },
        { key: 'category', value: '文章分类' },
        { key: 'tags', value: '标签' },
        { key: 'about', value: '关于我' },
        { key: 'links', value: '友情链接'},
      ],
    };
  },
};
</script>
<style>
.list-demo {
  font-size: 14px;
}
</style>

代码中含有 template,script,style。三者的作用此处就不在赘述,如上的结构展示了一个 vue 单文件基本的文件结构。其背后的理念就是一个单文件组件对应了一个功能性组件,该组件的模板,样式,业务逻辑都采用就近维护的思想。从组件的复用性,后期可维护性的角度上来说,这样的理念都大大的提高了组件化的开发效率。vue 的单文件,既不是 js,也不是 html,也不是 css 文件,这样的文件如何被应用到页面上,这也就是下面将会说到的一个问题,vue 单文件是如何被处理成页面中可用的资源。

vue 单文件被处理的流程

vue 单文件配合 webpack 构建工具,在 webpack 中会交由 vue-loader 来处理。如下所示:

{
  test: /\.vue$/,
  loader: 'vue-loader',
}

项目中通过 import 或者 require 引入的 vue 单文件,都会经过 vue-loader 处理,vue-loader 在这个过程中会将模板按照 template、script、style 解析并将处理结果返回,三种不同类型的文件交由接下来的loader 进行处理。如果该单文件组件在父组件中的 components 声明,则 components 中对应的该项会被插入解析后 script 代码。这个过程从入口文件 main.js 开始,所有涉及的被依赖单文件组件依次经历这样的处理过程。之后所有的组件的实例化将根据业务逻辑中的依赖关系进行,这个过程也是我们平时在开发中经常用到的一种方式。(这里可以单拉一篇文章详细讲述 vue-loader 的处理流程)

单文件的常用姿势

模板中的组件引用

一、使用方式

组件的拆分和嵌套:

  • 将具体的业务按照功能以及后期复用性方面的考虑划分成更小的组件
  • 通过一个容器组件(父组件)将小的功能组件(子组件)进行整合

操作手法:父组件中引入子组件,components 中注册,template 中添加相应的组件引用模板

这种方式也是我们在进行单文件的开发中常用的一种方式,所有组件的实例化,都被隐含在组件的嵌套关系和业务逻辑中。开发者只需要关心组件的引入,在父组件逻辑中注册该组件,并在父组件的模板中以标签的方式引入组件。这个过程中待引入的组件的实例化时机也可以通过 v-if 指令在业务逻辑中进行控制。

二、适用场景

大部分场景下我们都可以通过这样的方式进行组件化的开发。这种模式的有一个特点: 组件的引入通过组件注册和模板中写入对应的组件的标签来完成。模板中通过标签来引入组件这一步必不可少,这个特点在某些业务场景下可能给开发者带来了一定的重复工作量。

API 式的调用

API 式的调用指的是手动创建子组件的实例,业务逻辑中无需引入组件和模板标签占位,在暴露的 API 中控制组件的实例化与显示。

一、使用方式

  • 功能模块提供一个入口 js 来控制该功能模块下单文件实例的所有功能逻辑
  • 其他组件中使用该功能模块时,调用功能模块下的 js,传入部分参数

操作手法:

Confirm.vue

<template>
  <el-dialg
    title="test"
    :visible.sync="visible">
    {{content}}
    <el-button @click="handleCancelClick">cancel</el-button>
    <el-button @click="handleOkClick">ok</el-button>
  </el-dialg>
</template>
<script>
export default {
  name: 'Confirm',
  data() {
    return {
      visible: false,
      content: '这是一个confirm dialog',
      callback: null,
    };
  },
  methods: {
    handleCancelClick() {
      this.callback('cancel');
    },
    handleOkClick() {
      this.callback('confirm');
    },
  },
};
</script>

confirm.js

import Vue from 'vue';
import Confirm from './confirm';
const ConfirmConstructor = Vue.extend(Confirm);
const confirm = (content) => {
  let confirmInstance = new ConfirmConstructor({
    data: {
      content,
    },
  });
  confirmInstance.vm = confirmInstance.$mount();
  confirmInstance.vm.visible = true;
  // 手动插入目的 dom
  document.body.appendChild(confirmInstance.vm.$el);
  confirmInstance.vm.callback = action => {
    return new Promise((resolve, reject) => {
     resolve(action);
    });
  };
  return confirmInstance.vm;
};

如上所示,给出的是一个确认弹框的场景实现。确认弹框在很多用户交互中是一个必须的交互形式。很多组件库也采用上面这种 API 式的组件调用。调用方仅仅通过 api 的调用,就能实现该功能模块的引用。这样就避免了在 template 中通过标签占位的方式引用。实现原理就是手动接管单文件组件的实例化,通过 Vue.extend 获得该组件对应的 Vue 的子类,在暴露给调用的 api 中去实例化这个组件。这个过程中我们可能还要完成一些组件数据的注入,逻辑相关以及手动将该组件插入到目的 dom 中。手动的注入 dom 是该种方式的一个很大特点,通过在 api 中动态的注入目的 dom,避免我们在各个业务组件中调用该功能模块时重复性的在业务组件 template 中手写组件标签。

二、适用场景

  • 功能聚合度高,组件内逻辑简单,输入输出较为单一,比如一些功能较为独立的弹框
  • 一些特殊的自定义指令开发,比如在一些特殊场景的指令,可以复用一些单文件组件,通过在指令的钩子中实例化组件对应的 vue 子类,按照特定的逻辑插入到目的 dom 中(例如:element-ui的v-loading)

区别和共性

共性:通过实例化对应组件完成组件的功能逻辑

区别:实例化的时机和方式不同。模板式的引入通过组件注册和标签引入的方式来使用单文件组件。标签引入解决了子组件插入的 dom 位置问题,开发者无需关心。API 式的单文件组件使用,在 API 调用时手动实例化组件,需要手动控制插入到目的 dom。

总结

vue 的单文件组件提供了 vue 的组件化开发思路,其本质在导出 vue 的一些关键属性,比如生命周期函数,methods,computed, watch,props等。我们可以通过上述两种方式来使用单文件组件,目的在于工程内部尽量减少重复的模板代码,组件解耦。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
使用js显示当前时间示例
Mar 02 Javascript
jquery实现的一个文章自定义分段显示功能
May 23 Javascript
node.js使用npm 安装插件时提示install Error: ENOENT报错的解决方法
Nov 20 Javascript
浅谈jQuery中的事件
Mar 23 Javascript
jQuery实现带有上下控制按钮的简单多行滚屏效果代码
Sep 04 Javascript
JavaScript中字符串与Unicode编码互相转换的实现方法
Dec 18 Javascript
基于javascript实现页面加载loading效果
Sep 15 Javascript
js事件处理程序跨浏览器解决方案
Mar 27 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
May 19 Javascript
AngularJS中transclude用法详解
Nov 03 Javascript
使用jQuery和ajax代替iframe的方法(详解)
Apr 12 jQuery
Mpvue中使用Vant Weapp组件库的方法步骤
May 16 Javascript
快速解决vue动态绑定多个class的官方实例语法无效的问题
Sep 05 #Javascript
jQuery扩展方法实现Form表单与Json互相转换的实例代码
Sep 05 #jQuery
vue中动态添加class类名的方法
Sep 05 #Javascript
ng-events类似ionic中Events的angular全局事件
Sep 05 #Javascript
vue获取元素宽、高、距离左边距离,右,上距离等还有XY坐标轴的方法
Sep 05 #Javascript
vue 监听屏幕高度的实例
Sep 05 #Javascript
Vue-Router的使用方法
Sep 05 #Javascript
You might like
PHP 获取文件路径(灵活应用__FILE__)
2013/02/15 PHP
php从数组中随机选择若干不重复元素的方法
2015/03/14 PHP
php获取网站百度快照日期的方法
2015/07/29 PHP
laravel高级的Join语法详解以及使用Join多个条件
2019/10/16 PHP
如何在Mozilla Gecko 用Javascript加载XSL
2007/01/09 Javascript
JavaScript Event学习第三章 早期的事件处理程序
2010/02/07 Javascript
简单的jquery拖拽排序效果实现代码
2011/09/20 Javascript
jQuery常用数据处理方法小结
2015/02/20 Javascript
浅谈js中字符和数组一些基本算法题
2016/08/15 Javascript
Javascript使用SWFUpload进行多文件上传
2016/11/16 Javascript
原生JS实现图片懒加载(lazyload)实例
2017/06/13 Javascript
vue中v-for加载本地静态图片方法
2018/03/03 Javascript
vue.js 嵌套循环、if判断、动态删除的实例
2018/03/07 Javascript
Node.js使用cookie保持登录的方法
2018/05/11 Javascript
JavaScript禁用右键单击优缺点分析
2019/01/20 Javascript
原生JS无缝滑动轮播图
2019/10/22 Javascript
Node.JS如何实现JWT原理
2020/09/18 Javascript
uniapp实现横向滚动选择日期
2020/10/21 Javascript
JavaScript this关键字的深入详解
2021/01/14 Javascript
Python基于pygame实现的font游戏字体(附源码)
2015/11/11 Python
python 线程的暂停, 恢复, 退出详解及实例
2016/12/06 Python
Python cookbook(数据结构与算法)实现对不原生支持比较操作的对象排序算法示例
2018/03/15 Python
Django项目中model的数据处理以及页面交互方法
2018/05/30 Python
浅析matlab中imadjust函数
2020/02/27 Python
详解使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)
2020/04/01 Python
施华洛世奇英国官网:SWAROVSKI英国
2017/03/13 全球购物
土建专业毕业生自荐书
2014/07/04 职场文书
六查六看自检自查剖析材料
2014/10/14 职场文书
怎样写离婚协议书
2015/01/26 职场文书
荆州古城导游词
2015/02/06 职场文书
我的1919观后感
2015/06/03 职场文书
呼兰河传读书笔记
2015/06/30 职场文书
家长必看:义务教育,不得以面试 评测等名义选拔学生
2019/07/09 职场文书
导游词之无锡华莱坞
2019/12/02 职场文书
python实现网络五子棋
2021/04/11 Python
Mysql 如何查询时间段交集
2021/06/08 MySQL