front-end-navigator icon indicating copy to clipboard operation
front-end-navigator copied to clipboard

Vue2.0 中 render 和 template 疑惑点

Open pfan123 opened this issue 8 years ago • 2 comments

从 Vue 升级到2.0之后,多了采用jsx方式渲染生成HTML模版,还有一种就是一直沿用的 template 字符串拼接方式生成模版,但其实隐含的还有 直接去挂载手动写入模版,Vue 的功能很强大多种方式有时也让我们感到到很😅

Vue2.0 生成 HTML 方式:

  • 1.使用 render 函数jsx方式渲染生成HTML模版
  • 2.使用 template 字符串拼接方式生成模版
  • 3.采用自定义标签,内嵌到 Vue 根实例挂载点(自定义标签影响html语义化,不推荐,但其实这个算是vue 1.0 采用 es5 写法遗留问题未借用babel)

注意: 对于挂载实例 不是采用 render 方式拼接模版,webpack 打包 vue 会报错,主要是由于webpack选择 vue 版本问题,详见 https://github.com/vuejs-templates/webpack/issues/215,2.0 Changes #2873

警告错误 [Vue warn]: You are using the runtime-only build of Vue where the template option is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

在解释这些之前,我们先搞清楚明白,组件 component 与 根实例 new Vue() 的关系:

创建 Vue 的根实例

const vm = new Vue({
  // 选项对象

  data: {},
  template: '<my-component></<my-component>',
  //render: (myComponent) => {
 	   return createElement(myComponent)
 	},

  components: {
  	"my-component": MyComponent 
  },

  created () => {

  },

  mounted () => {

  },

  methods () => {

  }

})

一个 Vue 实例其实正是一个 MVVM 模式中所描述的 ViewModel - 因此在文档中经常会使用 vm 这个变量名。 在实例化 Vue 时,需要传入一个选项对象,它可以包含数据、模板、挂载元素、方法、生命周期钩子等选项。

创建 component 组件

可以扩展 Vue 构造器,从而用预定义选项创建可复用的组件构造器(这个是官方说法,比较绕口),其实说白了,组件可以理解为继承于 Vue 构造器,与跟实例 vm 存在细微的差别对数据指针data更改。

//继承Vue 构造器,创建组件,区别修改data指针,跟实例 data: {},组件 data () {return {}} 
const MyComponent = Vue.extend({
  // 扩展选项对象
})

// 所有的 `MyComponent` 实例都将以预定义的扩展选项被创建,实例组件可以直接 data: {}, 没有指针问题,不会影响其他组件
const myComponentInstance = new MyComponent({
  data: {}
})

注册 component 组件

组件全局注册

// 要使用这个组件构造器做的组件,需要用 Vue.component(tag, consturctor) 注册--全局注册,注册在根实例上的,全局可以直接使用
// Vue.component('my-component', MyComponent)

注意:对于统一性、共用性组件,建议采用组件全局注册方式,如 <ant-img src = "" v-on:error="_errorReport()" v-on:load="_loadReport"></ant-img>替代image标签进行统一上报处理。

组件局部注册

// vue 创建组件默认,使用构造器,然后实例生成组件
const MyComponent = Vue.extend({
  
})

const myComponentInstance = new MyComponent({
	data: {},
	components: {
	  "my-component": MyComponent  //组件局部注册,注册在子组件上的
	}
})

有了以上知识之后,基本对vue的实例,组件思路有了整理的了解,那接下来理解 Vue2.0 生成 HTML 方式。

render 函数jsx方式渲染生成HTML模版

字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。render 函数接收一个 createElement 方法作为第一个参数用来创建 VNode

//根实例挂载
const vm = new Vue({
	//virtul Dom 方案渲染模版
	render: h => h(APP)

	//render: (createElement) => {
	//	 return createElement(APP)
	//}	
}).$mount('#app')

createElement 函数中生成模板:

createElement(
  // {String | Object | Function}
  // 一个 HTML 标签,组件选项,或一个函数
  // 必须 Return 上述其中一个
  'div',
  // {String | Array}
  // Children VNodes, built using `createElement()`,
  // or simply using strings to get 'text VNodes'. Optional.
  [
    'Some text comes first.',
    createElement('h1', 'A headline'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

template 字符串拼接方式生成模版

    // Vue.extend()组件构造器
    const MyComponent = Vue.extend({
      template: '<div>大家好,{{msg}}我来了</div>',
      data: function () {
        return {
          msg: 'Hello!'   //组件构造器生成的组件data数据需要return 返回,而实例new Vue()不需要(主要是处理指针问题)
        }
      },
      created: function(){
        console.log("组件开始创建")
      }
    })

    const vm = new Vue({
    	//template是字符串模版拼接方案渲染模版
    	template: '<my-component></my-component>',
	    components: {
	       "my-component": MyComponent  //实例局部注册组件
	    },
	    created(){
	    	console.log("开始创建")
	    }

    })

采用自定义标签,内嵌到 Vue 根实例挂载点获取模版

index.html

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
  <meta name="author" content="@pfan"/>
</head>

    <!-- S 左侧导航内容 -->
    <div vm-mod = "sideLeftNav">
      <my-component></my-component>
    </div>
    <!-- E 左侧导航内容 -->

    <!-- 这里填写路径是输出的路径 相对于dist输出路径 -->
    <!-- <script src="./app.js"></script> -->
  </body>
</html>
 

app.js

import Vue from 'vue'

var MyComponent = Vue.extend({
  template: '<div>大家好,{{msg}}我来了</div>',
  data: function () {
    return {
      msg: 'Hello!'  
    }
  },
  created: function(){
    console.log("组件开始创建")
  }
})

//全局注册,不需要在组件做局部注册
Vue.component('my-component', MyComponent)

const vm = new Vue({
	el: '[vm-mod="sideLeftNav"]', 

	//由于组件是全局注册,所以不需要局部注册,可直接使用
	components: {
	//	"my-component": MyComponent  //实例局部注册组件
	}
})

ps: 注意,自定义标签的这种模版方式不推荐使用,影响html语义化。

总结,通过以上的例子,我们理清了,组件与 Vue 根实例的关系, 以及挂载点,生成 html 的方式,最后更好的扩展优化可以深入做vue组件直出方案 prerender-spa-plugin 做简单直出。

参考资料:

官方 Vue.js 教程 极客 Vue.js 教程 Vue 组件 data 为什么必须是函数?

pfan123 avatar Jun 30 '17 11:06 pfan123

// 所有的 MyComponent 实例都将以预定义的扩展选项被创建 const myComponentInstance = new MyComponent({})

请问,这句是什么意思?vue内部哪个阶段执行的?

bi-kai avatar Sep 27 '18 02:09 bi-kai

@bi-kai const myComponentInstance = new MyComponent({}) 这里是实例化组件,并不是那个生命周期执行,挂载在 vue 根实例,complier之后才会有生命周期

pfan123 avatar Mar 26 '19 03:03 pfan123