Vue

Vue介绍

什么是Vue.js

  1. vue.js是目前最火的一个框架,React是最流行的一个框架(React除了开发网站,还可以开发手机App)
  2. Vue.js是前端的主流框架之一,和Angular.js、React.js一起,并称为前端三大主流框架
  3. Vue.js是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)
  4. 前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道

为什么要学习流行框架

  1. 企业为了提高开发效率;
  2. Vue.js能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,前端程序员只需要业务逻辑,不再关注DOM是如何渲染的了】
  3. 增强自己就业时候的竞争力:人无我有,人有我优

框架和库的区别

  1. 框架:是一套完整的解决方案;对项目的侵入性比较大,项目如果更换框架,则需要重新架构整个项目。如:node中的express
  2. 库(插件):提供某一个小功能,对项目的侵入性比较小,如果某个库无法完成某些需求,可以很容易切换到其他库实现需求

Node(后端)中的MVC与前端中的MVVC之间的区别

  • MVC是后端的分层开发概念

  • MVVM是前端的视图层的概念,主要关注于视图层分离,也就是说:MVVM把前端的视图层,分为了三部分:Model, View, VM ViewModel

  • MVVM

    • Model层:职能单一,只负责操作数据库,执行对应的sql语句,进行数据的CRUD(creat、read、update、delete)。M保存的是每个页面中单独的数据
    • View层:每当用户操作了界面,如果需要进行业务的处理,都会通过网络请求,去请求后端的服务器,此时,我们的这个请求,就会被后端的项目入口模块监听到。V就是每个页面中的HTML结构
    • VM是MVVM思想的核心:因为VM是M和V之间的调度者。每当V层想要获取后保存数据的时候,都要由VM做中间的处理
    • 取数据:M->VM->V;存数据:V->VM->M
  • 前端页面中使用MVVC的思想,主要是为了让我们开发更加方便,因为MVVM思想突出贡献了数据的双向绑定。注意:数据的双向绑定是由VM提供的


Vue的使用

简介

<!DOCTYPE html>
<html>
<head>
    <title>vue</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-COmpatible" content="ie=edge">
    <script type="text/javascript" src="vue.min.js"></script>
</head>
<body>
    <div id="app">  
        <!-- vue控制的区域就是V -->
        <!-- 注意:我们new出来的vue对象就是mvvm中的vm调度者 -->
        <!-- new的vue实例会控制这个区域的内容 -->
        <p>{{msg}}</p>
        <!-- 插值表达式,通过此方法,data提供的所有属性都能直接引用了 -->
    </div>

    <script type="text/javascript">
        //创建一个vue的实例
        //当我们导入包之后,在浏览器的内容中,就多了一个vue的构造函数
        var vm = new Vue({
            //当我们new一个vue对象的时候,有几个要配置的属性
            el:'#app'   //表示要控制id为app的这个元素
            //el表示当前我们new的这个vue要控制页面上的哪个区域

            data:{//这里的data就是mvvm中的m用来保存每个页面数据的对象
                //data属性中,存放的是el中要用到的数据
                msg: '欢迎学习vue'  //通过vue提供的指令,很方便的就能把数据渲染到页面上
                //程序员不再手动操作DOM元素了

            }
        })
    </script>
</body>
</html>

v-cloak v-text v-html

代码实例:

<!DOCTYPE html>
<html>
<head>
    <title>vue</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-COmpatible" content="ie=edge">
    <script type="text/javascript" src="vue.min.js"></script>
    <style type="text/css">
        [v-cloak] {
            display: none;
            /*只要使用了v-cloak这个属性的元素,都让它display为none*/
        }
    </style>
</head>
<body>
    <div id="app">  
        <p v-cloak>{{msg}}</p>
        <!-- 添加v-cloak属性,让vue没有加载完的时候,这个标签隐藏(不然可能会看到{{msg}}到其值的切换过程——即:插值表达式的闪烁问题) -->
        <h4 v-text='msg'></h4>
        <!-- v-text和插值表达式没有太大区别,但是v-text没有闪烁问题
        但是:插值表达式可以在前后放任意内容(即只会替换自己的占位符,不会将整个标签间的内容清空),
        而v-text会将其值完全覆盖住标签之间的内容
        此二者都会将其内容当作普通内容输出 -->
        <div>{{msg2}}</div>
        <div v-text='msg2'></div>
        <!-- 以上两者的输出一样,都是msg的原格式 -->
        <div v-html='msg2'>143</div>
        <!-- v-html会将内容输出为html格式,也会覆盖标签间的原本内容 -->
        <input type="button" v-bind:title="mytitle+'123'" name="">
        <!-- 如果不加v-bind,title的内容就只会是mytitle,而用v-bind进行了绑定后,其内容就是下面data中的myttitle的值。v-bind:可以简写为冒号":" -->
        <!-- 可以理解为属性和属性之间需要绑定 -->
        <!-- v-bind会将后面的(引号内的)当作js表达式去执行(即认为mytitle是一个变量),所以可以直接对其进行“加”操作 -->
        <!-- 亦即:v-bind中可以写合法的js表达式 -->
    </div>
    <script type="text/javascript">
        var vm = new Vue({  //注意:Vue的首字母大写,Vue中,每一语句以逗号分隔
            el:'#app',
            data: {
                msg:'123',
                msg2: '<h1>哈哈,我是h1</h1>',
                mytitle:'这是一个自己定义的title',
            }
        })
    </script>
</body>
</html>

结果如下:


在这里插入图片描述

使用v-on指令定义Vue中的事件

v-on相当于jquery中的click(原生js中的onclick)

  1. <input type="button" value="按钮" :title="mytitle + '123'" v-on:click="alert("hello")">:会弹出hello;如果是自己在methodes中定义的方法,用引号将方法名引起来
<!DOCTYPE html>
<html>
<head>
    <title>vue</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="./vue.min.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <input type="button" v-bind:title="mytitle+'123'" value='按钮' name="" v-on:click="show">
        <!-- 当点击的时候,调用show方法,注意:这里的show没有括号 -->
        <!-- 浏览器中常见的事件,如:moouseover等,都可以像clikc这样定义 -->
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data: {
                mytitle:'这是一个自己定义的title',
            },
            methods: {
                // js中,带s的一般是数组,但是这里比较特殊,是对象
                // 在methodes中定义了当前vue实例所有可用的方法
                show: function() {
                    // 定义了一个名字叫show的函数
                    alert('hello')
                }
            }
        })
    </script>
</body>
</html>

ps:v-bind绑定属性(缩写:":");v-on:绑定事件(缩写:"@")

案例:跑马灯效果

暂略

事件修饰符

  1. .top 阻止冒泡
  2. .prevent 阻止默认事件,比如a标签的跳转,表单的submit
  3. .capture 添加事件侦听器时使用事件捕获模式,默认是冒泡机制,从内而外,如果是用捕获机制,从外而内
  4. .self 只当事件在该元素本身(比如不是子元素,也不是父元素,即:冒泡或捕获不会触发)触发时触发回调
  5. .once 事件只触发一次

事件修饰符可以串联:@click.prevent.once:只阻止一次默认行为(代码的先后顺序无关)

.self与.stop的区别:.self只阻止自己的冒泡(比如:子元素产生的冒泡自己不执行,但是依旧将其继续向上冒泡),而.stop阻止所有的冒泡

用法:在事件后面加.修饰符

eg:利用修饰符阻止冒泡

<!DOCTYPE html>
<html>
<head>
    <title>阻止冒泡</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="./vue.min.js"></script>
    <style type="text/css">
        .inner {
            height: 150px;
            background-color: darkcyan;
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="inner" @click="divHandler">
            <input type="button" value="点击" @click.stop='btnHandler' name="">
        </div>
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data: {
                
            },  //各平行部分间用逗号分隔
            methods: {
                divHandler(){
                    console.log('触发了div的点击事件')
                },
                btnHandler(){
                    console.log('触发了btn的点击事件')
                }
            }
        })
    </script>
</body>
</html>

v-model实现双向数据绑定

只有这个属性可以实现双向数据绑定

<!DOCTYPE html>
<html>
<head>
    <title>数据的双向绑定</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="./vue.min.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <h4>{{ msg }}</h4>
        <input type="text" :value='msg' name="">
        <!-- v-bind把msg的值进行了绑定,但是它智能进行单向绑定,从M自动绑定到V -->
        <input type="text" v-model='msg' name="">
        <!-- 利用v-model实现了表单元素和Model的数据双向绑定 -->
        <!-- v-model只能运用在表单元素中,input select radio checkbox text 等-->
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data: {
                msg: '大家好',
            },  //各平行部分间用逗号分隔
            methods: {
                
            }
        })
    </script>
</body>
</html>

效果图:修改第二个文本框内的内容时,其他的内容会跟着自动修改

在这里插入图片描述

案例:计算器

<!DOCTYPE html>
<html>
<head>
    <title>数据的双向绑定</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="./vue.min.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <input type="text" v-model='n1' name="">
        <select v-model='opt'>
            <!-- 下拉选择框 -->
            <option value="+">+</option>
            <option value="-">-</option>
            <option value="*">*</option>
            <option value="/">/</option>
        </select>
        <input type="text" v-model='n2' name="">
        <input type="button" name="" @click="calc" value="=">
        <input type="text" name="" v-model="result">
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data: {
                n1:0,
                n2:0,
                result:0,
                opt:"+"
            },  //各平行部分间用逗号分隔
            methods: {
                calc(){
                    switch(this.opt){
                        case "+":this.result = parseInt(this.n1) + parseInt(this.n2) 
                        break;
                        case "-":this.result = parseInt(this.n1) - parseInt(this.n2) 
                        break;
                        case "*":this.result = parseInt(this.n1) * parseInt(this.n2) 
                        break;
                        case "/":this.result = parseInt(this.n1) / parseInt(this.n2) 
                        break;
                    }
                    // 以上代码也可以写成:this.result = eval('parseInt(this.n1)' this.opt 'parseInt(this.n2)'),利用eval将字符串当作代码执行
                    //但是应注意:这是投机取巧的方式,正式开发中应减少使用
                }
            }
        })
    </script>
</body>
</html>

在vue中使用样式

使用class样式

  1. 数组:<h1 :class="['red', 'thin']">这是一个h1</h1>,其中,red是一个样式类,thin也是一个样式类。h1后面的冒号是v-bind的缩写,注意:类名必须用引号,列表也必须用引号
  2. 数组中使用三元表达式:<h1 :class="['red', 'thin', isactive?'':'active']">这是一个h1</h1>,但是代码不易读
  3. 数组中使用嵌套对象:<h1 :class="['red','thin', {'active':isactive}]">这是一个h1</h1>,其中,isactive是一个布尔类型变量
  4. 直接使用对象:<h1: class="{'red':true,' italic':true, 'active':true, thin:true}">这是一个h1</h1>,true表示使用该样式,也可以写成变量的形式,如:<h1: class="{'red':flag,' italic':flag, 'active':flag, thin:flag}">这是一个h1</h1>,其中,flag是data中的一个属性。注意:类名不用引号也可以。以上代码中也可以将{...}写在data区域中,如:
<h1 :class="classObj">这是一个h1</h1>
......
var vm = new Vue({
    el:'#app',
    data:{
    classObj:{'red':flag,' italic':flag, 'active':flag, thin:flag},
    }
})

使用内联样式

  1. 直接在元素上通过:style的形式,书写样式,如:<h1 :style="{color: 'red', 'font-size': '50px'}">这是一个h1</h1>,如果属性里面有短横线,就不能省略引号,如果要省略,就必须写成驼峰法
  2. 将样式对象,定义到data中,并直接引用到:style中。在data上定义:data:{ h1Style:{color: 'red', 'font-size':'40px'}},在元素中,通过属性绑定的形式,将样式对象应用到元素中<h1 :style="h1Style">这是一个h1</h1>
  3. :style中通过数组,引用多个data上的样式对象,<h1 :style="[h1Syle1,h2Style2]">这是一个h1</h1>,其中,列表中的每一个style对象都在data中有定义h1Style:{xxx}

v-for指令的四种使用方式

  1. 迭代数组(包括对象数组)
<ul>
    <li v-for="(item,i) in list">索引:{{i}}---姓名:{{item.name}}---年龄:{{item.age}}</li>
</ul>

以上代码可以创建多个li标签;如果用不到索引值,i可以省略,此时小括号也可以省略

  1. 迭代对象中的属性
<div v-for="(val, key, i) in userInfo">{{val}}---{{key}}---{{i}}---</div>

ps:迭代对象属性时,第三个参数为索引,第一个为值,第二个为键

  1. 迭代数字
<p v-for="i in 10">这是弟{{i}}个p标签</p>

注意:用v-for迭代数组,从1开始

v-for的使用注意事项:在2.2.0+的版本里,当在组件中使用v-for时,key是必须的

案例:添加列表项

<!DOCTYPE html>
<html>
<head>
    <title>数据的双向绑定</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="./vue.min.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <label>Id:
            <input type="text" v-model="id" name="">
        </label>

        <label>Name:
            <input type="text" v-model="name" name="">
        </label>

        <input type="button" value="添加" name="" @click="add">
    
        <p v-for='item in list' :key='item'>
            <!-- key能够保证数据的唯一性,即:记住该key的选项,而不是单纯根据索引来“记住”一个选项 -->
            <!-- 注意:v-for循环的时候key属性只能使用number或者string -->
            <!-- 注意:key在使用的时候,必须使用v-bind绑定的形式指定key的值 -->
            <!-- 在组件中使用v-for循环的一些特殊情况中,如果v-for的循环出现问题,必须在使用v-for的时候,制定唯一的字符串或数字类型的key值 -->
            <input type="checkbox" name="">{{item.id}}:{{item.name}}
        </p>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data: {
                id:"",
                name:"",
                list:[
                    {
                        id:1, name:'li4'
                    },
                    {
                        id:2, name:"wang5"
                    },
                    {
                        id:3, name:"zhang3"
                    },
                ]
            },  //各平行部分间用逗号分隔
            methods: {
                add(){
                    this.list.push({id:this.id,name:this.name});
                }
            }
        })
    </script>
</body>
</html>

效果图如下:


在这里插入图片描述

v-if和v-show

二者都是控制元素的显示与隐藏

v-if的特点是每次都会重新删除或创建元素

v-show的特点:每次不会重新进行DOM的删除和创建操作,只是切换了元素的display:none属性;

因此,v-if有较高的切换性能消耗,v-show有较高的初始渲染消耗

如果元素涉及到频繁的切换,最好不要使用v-if;如果元素可能永远都不会给用户显示出来,最好用v-if

代码示例:利用v-if和v-show切换元素的显示与隐藏

<!DOCTYPE html>
<html>
<head>
    <title>数据的双向绑定</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="./vue.min.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <input type="button" @click="toggle" name="" value="按钮">
        <h3 v-if="flag">这是用v-if控制的元素</h3>
        <h3 v-show="flag">这是用v-show控制的元素</h3>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data: {
                flag:true,
            },  //各平行部分间用逗号分隔
            methods: {
                toggle(){
                    this.flag = !this.flag
                }
            }
        })
    </script>
</body>
</html>

品牌管理案例:知识点的综合运用及新增知识点讲解

在vue中,使用事件绑定机制,为元素制定处理函数的时候,如果加了小括号,就可以给函数传参了


过滤器

vue.js允许自定义过滤器,可被用做一些常见的文本格式化,过滤器可以用在两个地方:mustachc插值和v-bind表达式。过滤器应该被添加在javascript表达式的尾部,由“管道”符指示

过滤器的调用时候的格式:{{ name | nameope }} :在使用name的值之前,先调用nameope对其进行处理,并把处理的结果当成内容进行渲染,nameope即过滤器的名称

过滤器中的function,第一个参数已经固定死了,永远是过滤器管道符前面传过来的数据,参数名可以自己随便取,也可以设置默认参数

如果要对过滤器传参,就在过滤器(上面例子的nameope处)加小括号传参即可,但是应注意:过滤器的第一个参数永远是管道符前面的数据,因此传过的来的参数至少在第二个形参位置

过滤器还可以多次连续调用:如:{{msg | msgFormat1 | msgFormat2}}:把原始数据交给第一个过滤器处理,第一个过滤器处理后的数据再交给第二个过滤器处理,第二个过滤器处理后在拿去使用

ps:字符串的replace方法,第一个参数除了可以是字符串之外,还可以是正则表达式

模板字符串:

`$(y)-$(m)-$(d)`    //其中,y,m,d都是变量

全局过滤器

所谓全局过滤器,就是所有的vm实例都能够共享

全局过滤器的定义写在vue对象的同一级别

全局过滤器的定义:Vue.filter("过滤器的名称", function(){}),function表示要对传过来的数据进行什么样的处理

私有过滤器

私有过滤器写在vue对象的内部,与data同级别

定义方法:

filters:{   //在内部定义过滤器
    dateFormate:function(data, pattern){
        ......
    }
}

过滤器调用的时候是采用就近原则:如果私有过滤器和全局过滤器名称一致,优先调用私有过滤器(指在管那个标签的vue中定义的私有过滤器)

字符串的padStart方法

字符串.padStart(maxlength, fillstring):第一个参数为指定的宽度,第二个参数为填充字符,如果宽度不足,就在字符串的开头用该字符填充

与padstart方法相匹配的有padEnd()方法,用法与padstart一样,但是在字符串末尾填充

ps:数字.toString(),可以将数字转换成字符串格式

自定义按键修饰符

用法示例:

<input type='text' @keyup='add'>
//任意按键被点击时,调用add方法

如何指定某个固定键?格式如下:

<input type='text' @keyup.enter='add'>
//绑定了enter键,当点击enter键时,才调用add

常用按键别名:

  1. enter
  2. tab
  3. delete:获取删除和退格键
  4. esc
  5. up
  6. down
  7. left
  8. right
  9. space

如果是其他键,直接将对应的键盘码替换别名即可;为了方便,常将键盘码“起别名”

自定义全局按键修饰符

Vue.config.keyCodes.f2=113,

然后就可以将“f2”与enter,esc等按键一样直接用别名,注意:113是f2的键盘码

在vue中,所有的指令都以v-开头

自定义指令

自定义全局指令

使用Vue.directive()定义全局(与vue对象的定义处于同一级)的指令,在使用的时候,直接将v-指令名放在标签中即可

其中:

  1. 参数一:指令的名称,注意:在定义的时候,指令的前面不需要加v-前缀,但是在调用的时候,必须在指令的前面加上v-前缀进行调用
  2. 参数二:是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作

示例:

Vue.directive('focus',{
    bind:function(el){
    //el就是绑定到的元素,注意,在每个函数中,第一个餐具是,永远是el,这个el参数,是一个原生的js对象
    //在元素刚绑定指令的时候,还没有插入到DOM中去,这时候调用focus方法没有用
    el.focus(),
    },
    inserted:function(el){el.focus()
    //由于inserted方法只要父节点存在即可调用,不必插入document,所以可以在此处写focus,让页面一刷新,绑定的元素就自动获取焦点
    },
    updated:funcion(){}
})
  1. bind:只调用一次,指令第一次绑定到元素时调用,用这个函数可以定义一个在绑定时执行一次的初始化操作
  2. inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。通常而言,对于行为,一般写在inserted中,防止行为不生效;而如样式之类的一般写在bind中即可
  3. updated:做在的组件的VNode更新时调用,但是可能发生在其孩子的VNode更新之前,指令的值可能发生了改变也可能没有,但是你可以通过比较更新前后的值来忽略不必要的模板更新;可能触发多次
  4. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
  5. unbind:只调用一次, 指令与元素解绑时调用。

以上函数均被称为钩子函数,后两者一般用得较少

含参案例:

<input type="text" v-model="keywords" v-focus v-color="'blue'" name="">

其中,v-focus和v-color都是自定义指令,v-focus自动获得焦点,v-color设置字体颜色,可以传参,此处传的参数为“blue”,注意:外面的引号中还有引号,如果只有一个引号,就当变量

钩子函数的参数

  1. el:指令绑定的元素,可以用来直接操作DOM

  2. binding:一个对象,包括以下属性

    • name: 指令名,不包括 v- 前缀。
    • value: 指令的绑定值, 例如: v-my-directive="1 + 1", value 的值是 2。
    • oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression: 绑定值的表达式或变量名(即字符串格式)。 例如 v-my-directive="1 + 1" , expression 的值是 "1 + 1"。
    • arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 "foo"。
    • modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }。

自定义私有指令

写在vue对象中,格式如下:其他和全局相同

var vm = Vue({
    ......
    directives:{
        'fontWeight':{
            bind:function(){},
            inserted:function(){},
            ...
        }
    }
})

函数简写

大多数情况下,我们可能想在bind和update钩子上做重复动作,并且不想关心其他的钩子函数,可以这样写:

Vue.directive('color-switch',function(el,binding){
//注意:这个function等同于将代码写到了bind和update中去
    el.stylee.backgroundColor = biding.value
})

Vue实例的生命周期

  • 什么是生命周期:从Vue实例创建、运行、到销毁,总是伴随各种各样的事件,这些事件,统称为生命周期

  • 生命周期钩子:就是生命周期事件的别名,也就是生命周期函数

  • 主要的生命周期函数分类:

    • 创建期间的生命周期函数:

      • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好data和methods属性
      • created:实例已经在内存中创建了,此时data和methods已经创建好,此时还没有开始编译模板。如果要使用各个属性与方法,最早的时间即是此时
      • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中。在beforeMount执行的时候,页面中的元素,还没有被真正的替换过来,只是之前写的一些模板字符串,此时,页面还是旧的
      • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示,此时,页面上的东西就是最新的了。如果要通过某些和的ODM节点,最早要在mounted中运行。只要执行了mounted,就表示这个vue实例已经初始化完毕了,此时,组建已经脱离了创建阶段,进入到了运行阶段
    • 运行期间的生命周期函数:

      • beforeUpdate:当数据被更新,状态更新之前执行此函数,此时data中的状态值是最新的,但是节目和显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点
      • updated:实例更新完毕之后调用此函数,此时data中的和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了
      • 以上两事件会根据data数据的改变,有选择的触发,0次到多次
    • 销毁期间的生命周期函数:

      • beforeDestroy:实例销毁之前调用,在这一步,实例仍然完全可用。还没真正执行销毁过程
      • destroyed:Vue实例销毁后调用,调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁

这些方法,都可以在methods中重写,但是要注意其各个函数的执行时机(比如beforeCreate函数在调用时,data和methods都还未被初始化好)

主要分为:创建前后、挂载前后、更新前后、销毁前后

vue-resource发起get、post、jsonp请求

<!DOCTYPE html>
<html>
<head>
    <title>vue-resource</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 在vue的包后面再导入vue-resource,因为vue-resource依赖于vue -->
    <script type="text/javascript" src="https://cdn.bootcss.com/vue-resource/1.5.1/vue-resource.min.js"></script>
    <!-- vue-resource在vue上又挂载了 this.$http属性,可以通过this.$http.get/post/jsonp做各种事情 -->

    <!-- get请求地址:http://wwww.liulongbin.top:3005/api/getlunbo -->
    <!-- post请求地址:http://wwww.liulongbin.top:3005/api/post -->
    <!-- jsonp请求地址:http://wwww.liulongbin.top:3005/api/jsonp -->
    <!-- 这些服务器的地址均以失效,但是重要的是学会使用,所以笔者也就没去另外找 -->
</head>
<body>
    <div id="app">
        <input type="button" value="get请求" @click='getInfo' name="">
        <input type="button" value="post请求" @click='postInfo' name="">
        <input type="button" value="JSONP请求" @click='jsonpInfo' name="">
    </div>

    <script type="text/javascript">

        Vue.component('login', {
            template: '<h3>登录组件</h3>'
        })

        Vue.component('register', {
            template:'<h3>注册组件</h3>'
        })
        var vm = new Vue({
            el:'#app',
            data:{
            },
            methods:{
                getInfo(){ //发起get请求
                    this.$http.get('http://wwww.liulongbin.top:3005/api/getlunbo').then(function(result){ console.log(result.body)})
                    // get中的参数:第一个为请求的路径url,后面有可选参数
                    // 然后通过.then来拿到数据,then中,第一个参数为成功的回调函数,第二个为失败的回调函数
                    //只要有.then,就表明这个方法是用promise来封装的
                    // 成功的回调必须要传,失败的回调可选
                    // 通过result.body拿到服务器返回的数据
                },
                postInfo(){
                    this.$http.post('http://wwww.liulongbin.top:3005/api/post', {}, {emulateJSON:true}).then(result => {console.log(result.body)})
                    // post中的参数:第一个:请求的地址;第二个:要发送的数据;第三部分为可选参数
                    // 手动发起的请求默认没有表单格式,所以有的服务器处理不了
                    // 通过post方法的第三个参数,设置提交的内容类型为普通表单数据格式(emulateJSON:true)
                },
                jsonpInfo(){
                    // 发起jsonp请求
                    this.$http.jsonp('http://wwww.liulongbin.top:3005/api/jsonp').then(result=>{console.log(result.body)})
                }
            },
            components:{
            }
        });
    </script>
</body>
</html>

JSONP的实现原理:

  1. 由于浏览器的安全级别限制,不允许AJAX访问协议不同、域名不同、端口号不同的数据借口,浏览器认为这种访问不安全

  2. 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称做JSONP(注意:根据JSONP的实现原理,知晓JSONP只支持Get请求)

  3. 具体实现过程:

    1. 先在客户端定义 一个回调方法,预定义对数据的操作
    2. 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口
    3. 服务其数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一 调用这个方法的字符串,发送给客户端去解释执行
    4. 客户端拿到服务器返回的字符串之后,当作script脚本去解析执行,这样就能够拿到JSONP的数据了

vue中的动画

动画的四个时间点两个阶段:

  1. 进入阶段:v-enter(此时透明度为0)到v-ener-to(此时透明度为1),称做v-enter-active
  2. 离开阶段:v-leave(此时透明度为1)到v-leave-to(此时透明度为0),称做v-leave-active

因为v-enter和v-leave-to的透明度一样,所以可以写为一组,同理,中间的两个时间点也可以写做一组

使用过渡类名

<!DOCTYPE html>
<html>
<head>
    <title>动画</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style type="text/css">
        .v-enter, .v-leave-to { /*为什么一定要写v?因为这是vue里面的特有的样式来实现动画的*/
            /*进入之前,元素的起始状态,此时还没有开始进入,这是一个时间点*/
            /*v-leave-to,是动画离开之后的终止状态,此时动画已经结束了,*/
            opacity: 0;
        }

        .v-enter-active, .v-leave-active {
            /*进入和离开时候的时间段*/
            transition: all 0.4s ease;
        }

        /*用过度类名实现两组动画,就实现两组样式就可以了*/

    </style>
</head>
<body>
    <!-- 需求:点击按钮,让h3显示,再次点击按钮,让h3隐藏 -->
    <!-- 1. 使用transition元素,把需要被动画控制的元素包裹起来 -->
    <!-- transition是vue官方提供的元素 -->
    <!-- 2. 自定义两组样式,来控制transition内部的元素实现动画 -->
    <div id="app">
        <input type="button" value="切换" @click='toggle' name="">
        <transition>
            <h3 v-if='flag'>这是一个h3</h3>
        </transition>
    </div>

    <script type="text/javascript">


        var vm = new Vue({
            el:'#app',
            data:{
                flag:false
            },
            methods:{
                toggle(){
                    this.flag = !this.flag
                }
            },
            components:{
            }
        });
    </script>
</body>
</html>

效果图:点击时会淡入淡出

在这里插入图片描述

如何修改v-前缀:使用transition标签中的name属性,eg:name='my',就表示以后所有的前缀不再是v-而是my-,也可以用此法实现多组动画样式

使用第三方类实现动画

第三方类库:animate.css

视频中的代码是这样的,但是自己的效果没有出来。。。问题尚未解决

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="animate.css">
    <!-- 使用animate.css库 -->
    <!-- 入场动画:bounceIn    离场动画:bounceOut -->
    <style type="text/css">
        

    </style>
</head>
<body>

    <div id="app">
        <input type="button" value="切换" @click='toggle' name="">

        <transition enter-active-class='bounceIn' leave-active-class='bounceOut' :duration='300'>
            <!-- 上面的两个animated如果不写,就要在元素上写上class="animated" -->
            <!-- duration表明持续时间,单位毫秒;可以省略 -->
            <!-- duration还可以写成对象的形式:"duration="{enter:200, leave:500 }"" -->
            <h3 v-if='flag' class="animated">这是一个h3</h3>
        </transition>
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data:{
                flag:false
            },
            methods:{
                toggle(){
                    this.flag = !this.flag
                }
            },
        });
    </script>
</body>
</html>

钩子函数实现半场动画

所谓半场动画,就是只有进入的动画,或者只有退出的动画,用之前的两种无法做出这种效果

可以在属性中声明javascript钩子(也可以被称为动画的生命周期函数)

前四个是进入,后四个是离开

<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"


v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
</transition>

案例:利用钩子函数实现小球的半场动画

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="animate.css">
    <!-- 使用animate.css库 -->
    <!-- 入场动画:bounceIn    离场动画:bounceOut -->
    <style type="text/css">
        .ball {
            width: 15px;
            height: 15px;
            border-radius: 50%;
            background-color: red;
        }

    </style>
</head>
<body>

    <div id="app">
        <input type="button" value="加入购物车" name="" @click="toggle">
        <!-- 1. 使用transition元素把小球包裹起来 -->
        <transition
        @before-enter="beforeEnter"
        @enter="enter"
        @after-enter="afterEnter">
        <div class="ball" v-show="flag"></div>
        </transition>
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data:{
                flag:false
            },
            methods:{
                toggle(){
                    this.flag = !this.flag
                },
                beforeEnter(el){
                    // 注意:每一个函数上都有一个el属性
                    // 动画钩子函数的第一个参数表示要执行动画的那个元素是个原生的js dom对象
                    // 可以认为el是通过document.getElementById('')方式获取到的
                    // beforeEnter表示动画入场之前,此时动画尚未开始,可以在beforeEnter中设置开始动画之前的起始样式
                    el.style.transform = "translate(0,0)"//设置小球开始动画之前的起始位置

                },
                enter(el, done){
                    // 这句话没有实际的作用,但是如果不写,出不来动画效果
                    // 可以认为el.offsetWidth是强制动画刷新,当然,也可以是offsetTop,offsetHeight都可以
                    el.offsetWidth
                    // enter表示动画开始之后的样式,这里,可以设置小球完成动画之后的结束状态
                    el.style.transform = "translate(150px, 450px)"
                    el.style.transition = "all 1s ease"
                    done()  //done其实就是afterEnter函数,此句话的作用是动画完成后立即调用afterEnter,否则小球会停留一会儿
                },
                afterEnter(el){
                    // 动画完成之后,会调用afterEnter
                    //用这句话可以直接跳过后半场动画,当第二次再点击按钮的时候,flag还是从false变为true的过程
                    this.toggle()
                }
            },
        })
    </script>
</body>
</html>

效果图:


在这里插入图片描述

明明每次动画后都只是将小球隐藏了,但是为什么每次点击按钮的时候,小球还是从0,0位置出现?

答:因为一轮动画完成后,当我们重新点击按钮,又是一轮新的动画,此时会执行beforeEnter,就会重置小球的位置

使用transition-group元素实现列表动画

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="animate.css">
    <!-- 使用animate.css库 -->
    <!-- 入场动画:bounceIn    离场动画:bounceOut -->
    <style type="text/css">
        li {
            border: 1px dashed #999;
            margin: 5px;
            height: 35px;
            padding: 5px;
            font-size: 12px;
            line-height: 35px;
            width: 100%;
            /*absolute如果不指定宽度,默认是最小值*/
            /*所以在此处指定了宽度*/
        }

        .v-enter,
        .v-leave-to {
            /*此处是设置enter和leave-to节点的状态*/
            opacity: 0;
            transform: translateX(100px);
        }

        .v-enter-active,
        .v-leave-active {
            /*这里面是动画的过渡效果*/
            /*即两个阶段:进入和离开*/
            transition: all 0.6s ease;
        }
        /*下面的v-move和v-leave-active的配合使用,可以实现列表后续的元素渐渐地漂上来的效果*/
        .v-move {
            /*可以设置元素在位移时候的渐变*/
            /*为了让.v-move起作用,还必须在.v-leave-active中设置position:absolute;*/
            transition: all 0.6s ease;
        }
        .v-leave-active {
            position: absolute;
            /*absolute如果不指定宽度,默认是最小值*/
            /*所以在li中指定了宽度*/
        }

        li:hover {
            background-color: hotpink;
            transition: all 1s ease;
        }

    </style>
</head>
<body>
    <div id="app">
        <div>
            <label>
                id:<input type="text" v-model='id' name="">
            </label>
            <label>
                name:<input type="text" v-model='name' name="">
            </label>
            <input type="button" value="添加" @click="add" name="">
        </div>
        <ul>
            <!-- 在实现列表过渡的时候,如果需要过渡的元素是通过v-for玄幻渲染出来的,不能使用transition包裹,需要使用transitionGroup -->
            <transition-group>
                <!-- 如果要给v-for循环创建的元素添加动画,必须为每个元素定义key属性 -->
                <!-- 注意:这里的transition-group不能写成驼峰式 -->
                <li v-for="(item,i) in list" :key="item.id" @click='remove(i)'>
                    {{item.id}}---{{item.name}}
                </li>
            </transition-group>
        </ul>
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el:'#app',
            data:{
                id:'',
                name:'',
                list: [
                {id:1, name:'赵高'},
                {id:2, name:'秦桧'},
                {id:3, name:'严嵩'},
                {id:4, name:'魏忠贤'}]
            },
            methods:{
                add(){
                    this.list.push({id:this.id,name:this.name})
                    this.id=''
                    this.name=''
                },
                remove(i){
                    this.list.splice(i,1)
                    // splice方法,从第一个参数处开始删除,删除第二个参数个
                }
            },
        })
    </script>
</body>
</html>

效果图:


在这里插入图片描述

vue组件

什么是组件:组件的出现,就是为了拆分Vue实例的代码量,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件

组件化和模块化的区别:

  1. 模块化是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一
  2. 组件化是从ui界面的角度进行划分的;前端的组件化,方便ui组件 使用

定义vue组件

注意:无论是哪种方式创建的组件,组件的template属性指向的模板内容,必须有且只有一 个根标签

全局组件定义的三种方式

全局组件在任意Vue控制的区域内都可以使用

  1. 使用Vue.extend配合Vue.componenet方法
var login_obj = Vue.extend({
    template:'<h1>登录</h1>'
});
Vue.component('login',login_obj);//将login_obj组件对象注册为名为login的组件

案例:

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <!-- 如果要使用组件,直接把组件的名称,以HTML标签的形式,引入到页面中即可 -->
        <my-com></my-com>
    </div>
    <script type="text/javascript">
        var mycom = Vue.extend({
            template: '<h1>这是使用Vue.extend创建的组件</h1>'
            // 通过template属性,指定了组件要战士的HTML结构
        })
        //1.2 使用Vue.component('组件的名称',创建出来的组件模板对象)
        Vue.component('myCom',mycom)//在注册的时候可以使用驼峰,但是在使用的时候,需要写成连字符+小写

        var vm = new Vue({
            el:'#app',
            data:{
            },
            methods:{
            },
        });
    </script>
</body>
</html>

如果取消中间变量,则可以写成:

Vue.component('register',Vue.extend({
    template:'<h1>注册</h1>'
}));

第一个参数是组件的名称,将来在引用组件的时候,就是一个标签形式来引入它
第二个参数:Vue.extend创建的组件,其中,template就是组件将来要展示的内容

  1. 直接使用Vue.component方法,给它传一个字面量的对象

还是使用标签的形式引用自己的组件

Vue.component('register',{
    template:'<h1>注册</h1>'
});
  1. 将模板字符串,定义到script标签中
<script id="tmpl" type="x-template">
    <div><a href="#">登录</a> | <a href="#">注册</a></div>
</script>

同时,需要使用Vue.component来定义组件

Vue.component('account',{
    template:'#tmpl'
});

案例:

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <!-- 在被控制的app外,使用template元素,定义组件的HTML模板结构 -->
    <template id="tmpl">
        <!-- 这里面还是只能有一个根标签 -->
        <div>
            <h1>这是通过template元素,在外部定义的组件结构,这个方式有代码的提示和高亮:个人理解,相当于把已经写好的模板直接当做组件注册</h1>
            <h4>相当好用</h4>
        </div>
    </template>
    <div id="app">
        <mycom></mycom> 
        <!-- 在被控制的区域内使用模板 -->
    </div>
    <script type="text/javascript">
        Vue.component('mycom',{
            template:'#tmpl'    //就是id为tmpl的标签
        })

        var vm = new Vue({
            el:'#app',
            data:{

            },
            methods:{

            },
        });
    </script>
</body>
</html>

效果:


在这里插入图片描述

实例内部私有组件的定义

ps:凡是私有的,都只能在自己的控制区域使用

定义:

var vm = new Vue ({
    el:'#app',
    data:{},
    methods:{},
    components:{
        组件名:{
            template:"<h1>这是私有的组件,也是一个HTML字符模板即可</h1>"
        }
    }
})

案例:

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <login></login>
    </div>
    <script type="text/javascript">

        var vm = new Vue({
            el:'#app',
            data:{

            },
            methods:{

            },
            components:{
                // 定义实例内部的私有组件
                login:{
                    template:"<h1>这是私有的login组件</h1>"
                }
            }
        });
    </script>
</body>
</html>

效果图:


在这里插入图片描述

当然,也可以将模板提到自己的控制区外单独定义

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <template id="priTmp">
        <div>
            <h1>这也是私有的组件</h1>
            <h2>但是提到了外面进行定义</h2>
        </div>
    </template>
    <div id="app">
        <login></login>
    </div>
    <script type="text/javascript">

        var vm = new Vue({
            el:'#app',
            data:{

            },
            methods:{

            },
            components:{
                // 定义实例内部的私有组件
                login:{
                    template:"#priTmp"
                }
            }
        });
    </script>
</body>
</html>

效果图:


在这里插入图片描述

其他定义组件的方法

1. 通过对象字面量的形式
var login = {   //login就是一个组件模板对象
    template:'<h1>23</h1>'
}
Vue.component('mylogin',login)  //定义全局

2.也可以用上面的login对象定义私有的组件
var vm=new Vue({
    el:...
    data:{...}
    methods:{...}
    components:{
    mylogin:login   //注意:此处还可以就写一个login,其他的啥都不写,此种方法说明,此处定义了一个变量,这个变量会被解析为属性,它的属性名和变量名相同(即属性值和变量名相同)
    }
})

组件中的data

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <mycoml></mycoml>
    </div>
    <script type="text/javascript">
        Vue.component('mycoml',{
            template:'<h1>这是一个全局组件,并且在这里引用了自己定义的数据(使用插值表达式):{{msg}}</h1>',
            data(){
                // 组件可以有自己的data数据
                // 组件的data和实例的data不一样,实例中的data可以为一个对象,但是组件中的data必须是一个方法
                // 组件中的data除了必须为一个方法之外,这个方法内部还必须返回一个对象
                //组件中的data数据,使用方式和实例中的data使用方式完全一样
                return {
                    msg: '这是组件中自己定义的数据'
                }
            }
        })
        var vm = new Vue({
            el:'#app',
            data:{
            },
            methods:{
            }
        });
    </script>
</body>
</html>

效果图:


在这里插入图片描述

组件中的data为什么要是一个function

案例:data中返回一个外部定义的对象

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <template id="tmpl">
        <div>
            <input type="button" value="+1" @click='increment' name="">
            <h3>{{count}}</h3>
        </div>
    </template>
    <div id="app">
        <counter></counter>
        <counter></counter>
        <counter></counter>
    </div>
    <script type="text/javascript">
        var dataObj = { count:0}
        // 在外面定义一个对象,其中的count值为0
        Vue.component('counter', {
            // 这是一个计数器的组棬,身上有个按钮,每当点击按钮,让data中 count 值 +1
            template: '#tmpl',
            data(){
                return dataObj
                通过此法也实现了返回一个对象
            },
            methods:{
                increment(){
                    // 定义一个increment方法
                    this.count++
                    // 因为在data中定义了dataObj,所以这里可以直接使用this对count进行访问
                    // 实际上,直接把data对象放在return中是一样的
                }
            }
        })

        var vm = new Vue({
            el:'#app',
            data:{

            },
            methods:{

            },
            components:{
                // 定义实例内部的私有组件
            }
        });
    </script>
</body>
</html>

效果图:


在这里插入图片描述

此种方法的弊端,当有多个模板实例的时候,返回的是同一个对象,所以他们共享这个对象的属性

所以在return时,一定要是一个新的对象,写成形如:return { count: 0} 的形式就能保证每一个实例对象的data都是独有的,不共享

不同组件间的切换

利用v-if和v-else实现组件的切换

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <a href="#" @click.prevent='flag=true'>登录</a>
        <a href="#" @click.prevent='flag=false'>注册</a>
        <login v-if='flag'></login>
        <!-- 当flag为真的时候显示这个组件 -->
        <register v-else='flag'></register>
        <!-- 否则显示这个组件 -->
    </div>

    <script type="text/javascript">

        Vue.component('login', {
            template: '<h3>登录组件</h3>'
        })

        Vue.component('register', {
            template:'<h3>注册组件</h3>'
        })
        var vm = new Vue({
            el:'#app',
            data:{
                flag:false
            },
            methods:{

            },
            components:{
            }
        });
    </script>
</body>
</html>

效果图:


在这里插入图片描述

当点击登录或注册文字时,下方的显示文字会切换

利用:is实现组件的切换

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <a href="#" @click.prevent="comName='login'">登录</a>
        <a href="#" @click.prevent="comName='register'">注册</a>
        <!-- component元素也是vue提供的,来展示对应名称的组件,is后面的值应该是组件的名称 -->
        <!-- component是一个占位符,:is属性,可以用来指定要展示的组件的名称 -->
        <component :is='"login"'></component>
        <!-- 组件绑定时,后面引号内的内容会当作表达式来解析,而不是单纯的当作字符串,如果要当作字符串,要在加一层引号 -->
        <!-- 要实现切换效果,将其用变量指向即可 -->
        <div>----------分隔符----------</div>
        <component :is='comName'></component>
    </div>

    <script type="text/javascript">

        Vue.component('login', {
            template: '<h3>登录组件</h3>'
        })

        Vue.component('register', {
            template:'<h3>注册组件</h3>'
        })
        var vm = new Vue({
            el:'#app',
            data:{
                comName:'register'
            },
            methods:{

            },
            components:{
            }
        });
    </script>
</body>
</html>

效果图:点击最上方的文字是,最下面的文字会切换


在这里插入图片描述

组件的切换动画

  1. 直接使用transition标签将组件包裹起来,如
<transition>
    <mycompl></mycompl>
</transition>
  1. 加上两个节点和两个阶段的样式
.v-enter,.v-leave-to {}
.v-enter-active, .v-leave-active {}

ps:在transition标签中,可以指定模式,例如:<transition mode='out-in“></transition>,就能够让动画先播放出去的,再播放进来的。比如在如上的组件切换中,就能够让旧的组件先出去,然后新的组件再进来,而不是旧的还没出去,新的就已经在进来了

父子组件的传值

父组件向子组件传值

  1. 组件实例定义方式,注意:一定要使用props属性来定义父组件传递过来的数据
<script>
    var vm = new Vue({
        el:'#app',
        data:{
            msg:'这是父组件中的消息'
        },
        methods:{},
        components:{
            son:{
                template:'<h2>这是子组件...{{finfo}}</h2>'
                props:['下面的自定义属性名']
            }
        }
    })
</script>
  1. 使用v-bind或简化指令,将数据传递到子组件中
<div id='app'>
    <son :自定义属性名='msg'></son>
</div>

案例:

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="animate.css">
    <style type="text/css">
    </style>
</head>
<body>
    <div id="app">
        <!-- 父组件可以在引用子组件的时候,通过属性绑定的形式,把需要传递给子组件的数据,以属性绑定的形式,传递给子组件内部,供子组件使用 -->
        <com1 v-bind:parentmsg='msg'></com1>
        <!-- 为com1组件绑定了一个叫做parentmsg的自定义属性,通过此方法,就通过自定义属性的方法传递到子组件内部去 -->
        <!-- 在子组件内部就可以拿到parentmsg -->
        <!-- 但是这样子组件还无法直接使用parentmsg -->
        <!-- 如果子组件要使用parentmsg这个属性,还需要在自己内部定义一下 -->
    </div>

    <script type="text/javascript">
        var vm = new Vue({//此中,vm是父组件,com1是子组件
            // 在js中,vm相当于父级作用域,com1相当于子级作用域,子作用域是可以访问到父作用域中的数据的
            el:'#app',
            data:{
                msg:'123a,父组件中的数据'
            },
            methods:{
                
            },
            components:{
                com1:{
                    data(){
                    return {    

                    }
                },
                props:['parentmsg'],// 注意:组件中的所有props中的数据,都是通过父组件传递给子组件的。
                // 而子组件中的data中的数据都是自己私有的,不是父组件传过来的。
                // data中的数据都是可读可写的,props中的数据都是只读的
                // 通常,在vm对象,或者说在组件内部带s的都是对象,比如directives,filters。只有props是一个数组
                // 如果子组件要使用父组件中的属性,就需要在props中原封不动的定义一下
                    template:'<h5>这是子组件,尝试引用父组件中的数据(双大括号内写msg):{{msg}},但是失败了,提示msg未定义,即,默认情况下,子组件是不能访问到父级组件中的数据的。但是通过父组件传值的形式(将msg改写为上面的parentmsg),发现成功了,获得的数据为:{{parentmsg}}</h5>'
                }
            }
        })
    </script>
</body>
</html>

子组件通过事件调用向父组件传值

方法也是数据的一种,因此父组件也能够把方法传给子组件,利用父组件把方法传递给子组件,子组件就能够将自己的数据以参数的形式给该方法,从而让父组件获得该数据

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="animate.css">
    <style type="text/css">
    </style>
</head>
<body>
    <div id="app">
        <com2 v-on:func="show"></com2>
        <!-- 方法要传递给子组件的时候,只能通过事件绑定机制,自定义一个事件属性 -->
        <!-- com2就能够拿到一个方法,叫做func,就是show方法的引用传递给了func -->
        <!-- ps:v-on可以简写为@ -->
    </div>

        <template id="tmpl">
            <div>
                <h1>这是子组件</h1>
                <input type="button" value="这是子组件中的按钮,点击它,触发父组件" @click='myclick' name="">
            </div>
        </template>

    <script type="text/javascript">
        var com2 = {
            // 定义了一个字面量类型的模板对象
            template:'#tmpl',
            data(){
                return {
                    sonmsg:{
                        name:'li4',age:34
                    }
                }
            },
            methods:{
                myclick(){
                    // 当点击子组件的按钮时,如何拿到父组件传递过来的方法?
                    this.$emit('func',this.sonmsg)
                    // emit代表触发、调用的意思
                    // 如果传递过来的方法需要传参,则emit中从第二个参数起都是传递给第一个参数(即:调用的方法)的参数,eg:this.$emit('func',1,"hello")
                },
            }
        }

        var vm = new Vue({
            el:'#app',
            data:{
                msg:'123a,父组件中的数据',
                dataMsgFromSon:null
            },
            methods:{
                show(data){
                    console.log("调用了父组件身上的show方法"+data)
                    this.dataMsgFromSon = data
                }
            },
            components:{
                com2
            }
        })
    </script>
</body>
</html>

使用this.$refs来获取元素和组件

<!DOCTYPE html>
<html>
<head>
    <title>组件</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="animate.css">
    <style type="text/css">
    </style>
</head>
<body>
    <div id="app">
        <input type="button" value="获取元素" @click='getElement' name="">
        <h3 id="myH3" ref="myH3">哈哈,这是一个H3</h3>
        <!-- 因为h3只是单个的元素,所以这里的ref不带s -->
        <login ref='mylogin'></login>
        <!-- 组件也可以通过ref引用 -->
    </div>

    <script type="text/javascript">
        var login = {
            template:'<h1>这是登录组件</h1>',
            data(){
                return{
                    msg:'son msg'
                }
            },
            methods:{
                show(){
                    console.log('调用了子组件的方法')
                }
            }
        }

        var vm = new Vue({
            el:'#app',
            data:{
                
            },
            methods:{
                getElement(){
                    // ref是英文单词reference,是引用的意思
                    console.log(this.$refs.myH3.innerText)
                    console.log(this.$refs.mylogin.msg)
                    this.$refs.mylogin.show()
                }
            },
            components:{
                login
            }
        })
    </script>
</body>
</html>

效果图:


在这里插入图片描述

路由

什么是路由

  1. 后端路由:对于普通的网站,所有的超链是url地址,所有的url地址都对应服务器上对应的资源
  2. 前端路由:对于单页面应用程序来说,主要通过url中的hash(#)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现,即不会和服务器有交互
  3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由)

在vue中使用vue-router

使用方法:

  1. 导包
  2. 路由是为了组件的切换,所以应该创建子组件
  3. 创建一个路由对象,定义路由规则
  4. 路由要展示,在页面上要有容器——router-view.
  5. 官方建议使用<router-link to=''>去往某个路由

案例:当点击跳转不同的url时,显示不同的组件,并且被激活的链接采用不同的样式类

<!DOCTYPE html>
<html>
<head>
    <title>路由的基本使用</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 1. 安装vue-router路由模块 -->
    <script type="text/javascript" src="./vue-router.js"></script>
    <!-- 也是必须先导入vue,才能使用vue-router -->
    <style type="text/css">
        .router-link-active {
            /*用了router-link的标签都这个样式类。默认:被激活的链接会采用这个样式类*/
            /*如果要修改链接激活时使用的样式类名(即不再默认使用router-link-active样式类),可以通过路由的构造选项linkActiveclass来全局设置*/
            color: red;
            font-weight: bold;
            font-size: 30px;
        }

        .myActive { 
            /*用于设置链接激活的默认样式类*/
            color: yellowgreen;
            background-color: cyan;
        }

    </style>
</head>
<body>
    <div id="app">
        <!-- <a href="#/login">登录</a> -->
        <!-- 如果不加#,就表示跳转到真实的页面 -->
        <!-- <a href="#/register">注册</a> -->

        <router-link to='/login'>登录</router-link>
        <router-link to='/register'>注册</router-link>
        <!-- 这两句router-link是vue-router提供的,作用和上面注释掉的两个a标签作用相同 -->
        <!-- router-link默认渲染成a标签 -->
        <!-- 不管用tag指定其渲染为哪种标签,永远会给它绑定一个点击事件 -->


        <!-- router-view是vue-router提供的元素,专门有来当作占位符的,将来路由规则匹配到的组件就会展示到这个router-view中去,所以可以把它当作占位符 -->
        <router-view></router-view>     

    </div>

    <script type="text/javascript">
        var login={ //组件的模板对象
            template:'<h1>登录组件</h1>'
        }

        var register={  //组件的模板对象
            template:'<h1>注册组件</h1>'
        }

        // 路由是监听不同组件的切换的
        // 2. 创建一个路由对象,当导入vue-router包之后,在window全局对象中,就有了一个路由的构造函数,叫做VueRouter
        // 在new路由对象的时候,可以为构造函数,传递一个配置对象
        var RouterObj = new VueRouter({
            // route//这个对象中的route表示路由匹配规则
            routes:[//路由匹配规则
                // {path:'/', component:login}, //默认显示login组件,但是这种方法不推荐,建议使用下面的这种方法
                {path:'/', redirect:'/login'},  //这里的重定向和Node中的redirect是完全不同的。因为node中的redirect是属于服务器端的技术,而这里是客户端通过某种方式修改hash值

            // 每个路由匹配规则,都是一个对象,这个规则对象,身上有两个必须的属性
            // 属性1:path,表示监听哪个路由链接地址
            // 属性2:component,表示如果路由是前面匹配到的path,就展示componenet组件
                {path:'/login', component:login},   //如果url地址是指向login,就展示login组件
                // 注意:component的属性值,必须是一个组件模板对象,而不能是组件的引用名称
                // 组件的引用名称,仅限于在页面的HTML中引用
                {path:'/register', component:register}
            ],
            // linkActiveClass:'myActive',  设置自己的链接激活类,当链接激活时,该标签采用myActive样式类,而不再是默认是的router-link-active样式类
        })
        var vm = new Vue({
            el:'#app',
            data:{

            },
            methods:{

            },
            router:RouterObj    //将路由规则对象注册到vm实例上,用来监听url地址的变化,然后展示对应的组件
        })
    </script>
</body>
</html>

效果图:


在这里插入图片描述

在路由规则中定义参数

  1. 在规则中定义参数:{path:/register:id, component:register},使用query的方式访问

案例:

案例:

```html
<!DOCTYPE html>
<html>
<head>
    <title>在路由规则中定义参数</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <router-link to='/login?id=10&name=zhang3'>登录</router-link>
        <!-- 如果在路由中,使用查询字符串,给路由传递参数,则不需要修改路由规则的path属性 -->
        <router-link to='/register'>注册</router-link>

        <router-view></router-view>
    </div>
    
    <script type="text/javascript">
        var login = {
            template:"<h1>登录---{{$route.query.name}}:{{$route.query.id}}</h1>",
            created(){
                // 组件的生命周期钩子函数
                console.log(this.$route.query.id)
            }
        }
        var register = {
            template:"<h1>注册</h1>"
        }
        var router = new VueRouter({
            routes:[
                {path:'/login', component:login},
                {path:'/register', component:register},
            ]
        })
        var vm = new Vue({
            el:'#app',
            data:{
            },
            methods:{
            },
            router //由于属性名和属性值完全一样,所以可以简写成一个即可,完整写法为:router:router
        })
    </script>
</body>
</html>

效果图:


我们也可以看到 ,在$route对象的query中,有id和name两个属性,所以我们在login组件中直接取出并展示在了页面上

  1. 通过this.$route.params来获取路由中的参数
var register = Vue.extend({
    template:'<h1>注册组件---{{this.$route.params.id}}</h1>'
})

案例:

<!DOCTYPE html>
<html>
<head>
    <title>在路由规则传参方式2</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <router-link to='/login/223/li4'>登录</router-link>
        <!-- 由于在路由匹配规则中使用了占位符:id,因此这里的223会被当作id进行解析 -->
        <router-link to='/register'>注册</router-link>

        <router-view></router-view>
    </div>


    <script type="text/javascript">

        var login = {
            template:"<h1>登录---{{$route.params.name}}---{{$route.params.id}}</h1>",
            created(){
                console.log(this.$route.params.id)
                console.log(this.$route.params.name)
            }
        }
        
        var register = {
            template:"<h1>注册</h1>"
        }

        var router = new VueRouter({
            routes:[
                {path:'/login/:id/:name', component:login},
                // 冒号表示占位符,意思是将来这个位置的东西当做id解析出来
                {path:'/register', component:register},
            ]
        })
        var vm = new Vue({
            el:'#app',
            data:{
            },
            methods:{
            },
            router 
        })
    </script>
</body>
</html>

效果图:


使用children属性实现路由嵌套

<!DOCTYPE html>
<html>
<head>
    <title>路由的嵌套</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <router-link to='/account'>account</router-link>
        <router-view></router-view>


    </div>

        <template id="tmpl">
            <div>
                <h1>这是Account组件</h1>
                <router-link to='/account/login'>登录</router-link>
                <router-link to='/account/register'>注册</router-link>
                <router-view></router-view>
            </div>
        </template>
    <script type="text/javascript">
        var account = {//组件的模板对象
            template:'#tmpl'
        }

        var login = {
            template:'<h3>这里是login</h3>'
        }

        var register = {
            template:'<h3>这里是register</h3>'
        }
        
        var router = new VueRouter({
            routes:[
                {path:'/account', component:account,
                    children:[
                        {path:'login', component:login},//注意,这里的login前面没有斜线,如果加了斜线,代表以根开始匹配
                        {path:'register', component:register}
                    ]
                },
                // 以下这种写法会导致login和account平级,如果点击account后再次点击登录或注册后,“这是Account组件”几个字会消失
                // 而我们想要的是登录或注册隶属于account,即不让文字消失
                // 所以应该在上面的一个规则中定义子路由
                // {path:'/account/login', component:login},
                // {path:'/account/register', component:register}
            ]
        })
        var vm = new Vue({
            el:'#app',
            data:{

            },
            methods:{

            },
            router 
        })
    </script>
</body>
</html>

效果图:


利用命名视图实现经典布局

<!DOCTYPE html>
<html>
<head>
    <title>命名视图-经典布局</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            background-color: gold;
        }

        .container {
            display: flex;
        }

        .left {
            flex: 2;
            /*意思是在这个盒子内,left占20%*/
            background-color: yellowgreen;
            height: 1000;
        }
        .main {
            flex: 8;
            background-color: hotpink;
            height: 1000px;
        }
    </style>
</head>
<body>
    <div id="app">
        <router-view></router-view> 
        <!-- 如果不指定组件的名称,就放default -->
        <div class="container">
            <!-- 为了实现flex布局,将其包起来 -->
            <router-view name='left'></router-view>
            <!-- 此处只放名称为left的组件 -->
            <router-view name='main'></router-view>
        </div>
    </div>

    <script type="text/javascript">
        var header = {
            template:'<h1 class="header">Header区域</h1>',
        }//在布局时,一个component对应一个“坑”——<router-view></router-view>
        var leftBox = {
            template:'<h1 class="left">leftBox区域</h1>',
        }
        var mainBox = {
            template:'<h1 class="main">mainBox区域</h1>',
        }

        var router = new VueRouter({
            routes:[
                {path:'/', components:{
                    'default':header,   //前面的属性名加不加单引号都可以,如果自己不加,会自动加上
                    'left':leftBox,
                    'main':mainBox,
                }},
            ],
        })

        var vm = new Vue({
            el:'#app',
            data:{
            },
            methods:{
            },
            router
        })
    </script>
</body>
</html>

效果图局部:


名称案例

使用@keyup监听按键的松开以实现对数据变动的监听

<!DOCTYPE html>
<html>
<head>
    <title>名字拼接</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <input type="text" v-model="firstname" name="" @keyup="getFullname">+
        <input type="text" v-model='lastname' name="" @keyup="getFullname">=
        <input type="text" v-model='fullname' name="">
    </div>

    <script type="text/javascript">
        
        var router = new VueRouter({
            routes:[
                
            ],
        })

        var vm = new Vue({
            el:'#app',
            data:{
                firstname:'',
                lastname:'',
                fullname:'',
            },
            methods:{
                getFullname(){
                    this.fullname = this.firstname + "-" + this.lastname
                }
            },
        })

    </script>
</body>
</html>

使用watch监听文本框数据的变化

<!DOCTYPE html>
<html>
<head>
    <title>命名视图-经典布局</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
        
    </style>
</head>
<body>
    <div id="app">
        <input type="text" v-model="firstname" name="" >+
        <input type="text" v-model='lastname' name="" >=
        <input type="text" v-model='fullname' name="">
    </div>

    <script type="text/javascript">
        
        var router = new VueRouter({
            routes:[
                
            ],
        })

        var vm = new Vue({
            el:'#app',
            data:{
                firstname:'',
                lastname:'',
                fullname:'',
            },
            methods:{
            },
            watch:{
                //使用这个属性,可以监视data中指定数据的变化,然后触发这个watch中对应的函数
                firstname:function(newVal, oldVal){
                    this.fullname = newVal + this.lastname
                },  //如果firstname发生了改变,就触发这个函数,属性名除非中间一短线连接,不然可以不加引号
                // function中默认提供了两个参数,newVal, oldVal
                lastname:function(newVal){
                    // 因为oldVal无用,所以声明时就不写了
                    this.fullname = this.firstname +  newVal
                }
            }
        })
    </script>
</body>
</html>

效果图:


watch的优点:可以监听看不到的数据的改变,比如路由的改变,就无法使用keyup进行监听,而watch就可以

watch监听路由地址的改变

路由地址:$route.path

<!DOCTYPE html>
<html>
<head>
    <title>路由的基本使用</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
        .router-link-active {
            color: red;
            font-weight: bold;
            font-size: 30px;
        }
        .myActive { 
            color: yellowgreen;
            background-color: cyan;
        }
    </style>
</head>
<body>
    <div id="app">
        <router-link to='/login'>登录</router-link>
        <router-link to='/register'>注册</router-link>
        <router-view></router-view> 
    </div>

    <script type="text/javascript">
        var login={ 
            template:'<h1>登录组件</h1>'
        }
        var register={  
            template:'<h1>注册组件</h1>'
        }
        var router = new VueRouter({
            routes:[
                {path:'/', redirect:'/login'},  
                {path:'/login', component:login},   
                {path:'/register', component:register}
            ],
        })
        var vm = new Vue({
            el:'#app',
            data:{
            },
            methods:{
            },
            router,
            watch:{
                '$route.path':function(newVal, oldVal){
                    if(newVal == '/login'){
                        alert('欢迎进入登录界面')
                    }else if(newVal == '/register'){
                        alert('欢迎进入注册页面')
                    }
                },
            }
        })
    </script>
</body>
</html>

当路由地址改变的时候,就会弹出弹窗

计算属性

案例:利用计算属性实现名字的拼接

<!DOCTYPE html>
<html>
<head>
    <title>命名视图-经典布局</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript" src="./vue-router.js"></script>
    <style type="text/css">
    </style>
</head>
<body>
    <div id="app">
        <input type="text" v-model="firstname" name="" >+
        <input type="text" v-model='lastname' name="" >=
        <input type="text" v-model='fullname' name="">
    </div>
    <script type="text/javascript">
        var router = new VueRouter({
            routes:[
                
            ],
        })
        var vm = new Vue({
            el:'#app',
            data:{
                firstname:'',
                lastname:'',
            },
            methods:{
            },
            computed:{  //在computed中,可以定义一些属性,叫做:计算属性,计算属性的本质就是一个方法,只不过我们在使用这些计算属性的时候,是把他们的名称直接当作属性使用的,并不会把计算属性当作方法去调用
                'fullname':function(){
                    return this.firstname + "-" + this.lastname
                    // 注意:计算属性在引用的时候,一定不要加小括号去调用,直接把它当作普通属性用即可
                    //只要计算属性这个fucntion内部,所用到的任何data中的数据发生了变化,就会立即重新计算这个计算属性的值
                    // 计算属性的求值结果会被缓存起来,方便下次直接使用,如果计算属性方法中,所用的任何数据,都没有发生过变化,则,不会重新对计算属性求值
                }
            }
        })
    </script>
</body>
</html>

watch、computed、methods的对比

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