Vue渲染方式

前言

所有代码运行Vue版本:2.5.16

Vue中的渲染方式个人总结可分为4种:

  • 原有模板语法,挂载渲染
  • 使用render属性,createElement函数直接渲染
  • 使用render属性,配合组件的template属性,createElement函数渲染
  • 使用render属性,配合单文件组件,createElement函数渲染

方式一

原有模板语法,挂载渲染:就是对使用Vue标签语法的hmtl进行渲染。

实例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TestVue</title>
</head>
<body>
<div id="app">
    <input v-model="message">
    <p>Message is: {{ message }}</p>
</div>
</body>
<script src="js/vue.js"></script>
<script type="text/javascript">
    var v = new Vue({
        el: '#app',
        data: {
            message: '这是内容'
        }
    });
</script>
</html>

特点

当页面返回时html中的v-model等属性并没有被渲染,保持不变发给客户端,客户端直到加载了Vue,创建了实例之后才会将这些标识渲染出来。

v-if显示后消失或消失后显示造成跳变

这种方式的缺点是明显的:浏览器第一时间无法识别这些标识!这会使得在某些情况下出现奇怪的现象,下面我修改代码以做演示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TestVue</title>
</head>
<body>
<div id="app">
    <template v-if="type === '1'">
        <div style="width: 400px;height: 300px;background-color: blue;"></div>
    </template>
    <div style="width: 400px;height: 200px;background-color: red;"></div>
</div>
</body>
<script src="js/vue.js"></script>
<script type="text/javascript">
    var v = new Vue({
        el: '#app',
        data: {
            type: '1'
        }
    });
</script>
</html>

可以看到上面的第一个div在type为1时应该显示,但是它在页面初始加载是不会显示的,一个html并没有<template>标签,整个标签会被跳过,那么这个页面展示的结果就会有一个一瞬间的跳变。

这个过程非常短,但是人眼是绝对识别得到的,在谷歌浏览器下使用Ctrl+Shift+R,360浏览器下使用Ctrl+F5强制不使用缓存加载页面我们就能看到这个跳变了!

注意另一种写法也会有跳变情况:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TestVue</title>
</head>
<body>
<div id="app">
    <div v-if="type === '1'" style="width: 400px;height: 300px;background-color: blue;"></div>
    <div style="width: 400px;height: 200px;background-color: red;"></div>
</div>
</body>
<script src="js/vue.js"></script>
<script type="text/javascript">
    var v = new Vue({
        el: '#app',
        data: {
            type: '2'
        }
    });
</script>
</html>

解释:div默认不会识别v-if默认显示,但是tpye为2,Vue加载后隐藏了该div,造成跳变。

如何解决跳变问题?

使用下面所有方法都可以解决,思路是用render函数进行渲染,而目标div内不含任何内容,也就是先什么都不显示,然后渲染出来正确的html,自然不会有跳变。

方式二

使用render属性,createElement函数直接渲染:原本无html,通过JavaScript 的完全编程的能力生成页面。

实例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TestVue</title>
</head>
<body>
<div id="app">
</div>
</body>
<script src="js/vue.js"></script>
<script type="text/javascript">
    var v = new Vue({
        el: '#app',
        data: {
            message: '这是内容'
        },
        render: function (createElement) {
            return createElement('p', 'Message is:' + this.message)
        }
    });
</script>
</html>

这里目标div内初始时没有任何内容,使用render方法生成内容,这里我没有渲染出v-model的效果,因为实现起来比较复杂,要用到组件,具体参考官网渲染函数 & JSX,Vue 的模板实际是就是被编译成了 render 函数。

这里render不要使用ECMAScript6语法如下:

render: h => h('p', this.message)

因为在ECMAScript6语法中,这里的this会指向[object Window],这个this.message是获取不到值的,而在上面一个的例子中this会指向当前Vue实例。

特点

极少会使用的方式,只适合一些细节渲染,虽完全控制输出,但不够直观,实现复杂,远不及模板标记语法:让Vue去帮我们编译渲染。

方式三

使用render属性,配合组件的template属性,createElement函数渲染。

相比于上一个方式,加入组件,利用template属性,更为直观,同样是原本无html,通过render函数渲染。

实例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TestVue</title>
</head>
<body>
<div id="app">
</div>
</body>
<script src="js/vue.js"></script>
<script type="text/javascript">
    var MyComponent = {
        data () {
            return {
                message: '这是内容'
            }
        },
        template: `
        <div id="app">
            <input v-model="message">
            <p>Message is: {{ message }}</p>
        </div>
        `
    };

    var v = new Vue({
        el: '#app',
        components: {
            'my-component': MyComponent
        },
        render: function (createElement) {
            return createElement('my-component')
        }
        //ECMAScript6: render: h => h('my-component')
    });
</script>
</html>

这里渲染组件时的渲染的内容会覆盖原<div id="app"></div>,而不是在其之下生成子节点。

特点

动态渲染,并且通过组件实现了模块分离,但是html模板被包在````里,使用不方便,IDE无法高亮代码,不适合大型项目。

下面是官网上对于这种方式的描述:

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

方式四 

说了前三种方式,其实只是一步步引出第四种方式:使用render属性,配合单文件组件,createElement函数渲染。

这种方式是绝大部分Vue项目(官方脚手架就是采用该渲染方式)的渲染方式。

在理解这一部分内容之前,你需要有webpack、vue-loader的知识:

1、webpack是核心,将组件文件打包,配合loader解析文件。

2、vue-loader是vue单文件组件的核心,负责解析.vue文件。

webpack以及loader

webpack,一种打包文件工具,配合一些插件可以模块化文件的打包处理等,我也是边学边做,自行百度了解。

loader,webpack非常常用的属性,让你能配合各种loader随意加载各种东西,看下官网给出的解释:

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

比如这里我们有.vue模块组件,我们要在另一个文件中import App from './App.vue'该如何做到,我一步步讲。

实现步骤

我是使用命令的方式先打包出一个目标文件,再运行服务器,好像可以写到package.json的'scripts'字段里做一个步骤的简化,我没试,还是最简单的方式吧。

1、我是用命令行打包所以先全局安装webpack,注意webpack4.0+版本后要附带安装一个webpack-cli或是webpack-command其中之一,我是安的webpack-command:

npm install webpack webpack-command -g

2、因为babel-loader、vue-loader等必须要有webpack,好像不在项目中载入webpack也不行,所以进入项目根目录(node_modules文件夹所在目录),npm install webpack --save在项目中加载webpack。

3、接着,再把需要的loader加载一下,首先是vue-loader,是解析.vue文件的核心loader,接着是babel-loader,它是用来修改不兼容的javaScript语法的loader,用来处理.vue文件中的<script></script>,还有处理<style></style>的css-loader、vue-style-loader,还有vue-loader强制附带个vue-template-compiler,我不知道是干嘛的。

加载babel:npm install babel-loader babel-core babel-preset-env --save

加载其他必要的:npm install vue-loader css-loader vue-style-loader vue-template-compiler --save

4、在根目录下新建一个名为dist的文件夹存放生成的文件。

5、在根目录下新建文件App.vue,这就是单文件组件了,而且是根组件,添加代码如下:

<template>
    <div id="app">
        <input v-model="message">
        <p>Message is: {{ message }}</p>
    </div>
</template>

<script>
export default {
    data () {
        return {
            message: 'dsdsadasad'
        }
    }
}
</script>

6、在根目录下新建webpack打包入口文件entry-client.js,添加代码如下:

import Vue from 'vue' //注意在静态编译时全都使用import,不要使用require,无法生效
import App from './App' //注意在静态编译时全都使用import,不要使用require,无法生效

const app = new Vue({
    components: {
        App
    },
    render: h => h(App)
});

app.$mount('#app');

7、在根目录下新建一个名为build的文件夹,在该文件夹内创建一个文件名为webpack.config.js的文件作为webpack配置文件,代码如下:

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
    entry: {
        'client.bundle': '../entry-client.js',
    },
    output: {
        path: path.resolve(__dirname, '../dist'), //生成文件放到dist目录下
        filename: '[name].js'
    },
    mode: 'development', //开发模式,有错误vue会在控制台提示
    resolve: {
        //加了下面这项我们就能将import App from './App.vue' 省略为import App from './App'
        extensions: ['.js', '.vue', '.json']
    },
    //设置loader以及插件,可参考vue-loader官网
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            // 它会应用到普通的 `.js` 文件
            // 以及 `.vue` 文件中的 `<script>` 块
            {
                test: /\.js$/,
                loader: 'babel-loader'
            },
            // 它会应用到普通的 `.css` 文件
            // 以及 `.vue` 文件中的 `<style>` 块
            {
                test: /\.css$/,
                use: [
                    'vue-style-loader',
                    'css-loader'
                ]
            }
        ]
    },
    plugins: [
        // 请确保引入这个插件来保证vue-loader的使用
        new VueLoaderPlugin()
    ]
};

8、命令行进入build文件夹,输入命令webpack打包文件,然后就可以在dist目录下看到生成的client.bundle.js目标文件了。

9、在根目录下创建测试文件index.html,添加代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TestVue</title>
</head>
<body>
<div id="app">
</div>
</body>
<script type="text/javascript" src="dist/client.bundle.js"></script>
</html>

特点

单文件组件,模块化,动态渲染,典型的单页面应用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容