实现数据的请求
then指定成功回调
真正的数据是在result.body中放着
除了 vue-resource 之外,还可以使用 axios 的第三方包实现实现数据的请求
axios是一个基于promise的HTTP库,可以用在浏览器和node.js中
请求地址
get请求地址: http://vue.studyit.io/api/getlunbo
post请求地址:http://vue.studyit.io/api/post
jsonp请求地址:http://vue.studyit.io/api/jsonp
JSONP的实现原理
由于浏览器的安全性限制,不允许AJAX访问 协议不同、域名不同、端口号不同的 数据接口,浏览器认为这种访问不安全。
可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP
具体实现过程:
先在客户端定义一个回调方法,预定义对数据的操作
再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口
服务器数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行
客户端拿到服务器返回的字符串之后,当作Script脚本去解析执行,这样就能够拿到JSONP的数据了
实例
const http = require('http'); // 导入解析 URL 地址的核心模块 const urlModule = require('url'); const server = http.createServer(); // 监听 服务器的 request 请求事件,处理每个请求 server.on('request', (req, res) => { const url = req.url; // 解析客户端请求的URL地址,截取parse部分,true表示用query的方法 var info = urlModule.parse(url, true); // 如果请求的 URL 地址是 /getjsonp ,则表示要获取JSONP类型的数据 if (info.pathname === '/getjsonp') { // 获取客户端指定的回调函数的名称 var cbName = info.query.callback; // 手动拼接要返回给客户端的数据对象 var data = { name: 'zs', age: 22, gender: '男', hobby: ['吃饭', '睡觉', '运动'] } // 拼接出一个方法的调用,在调用这个方法的时候,把要发送给客户端的数据,序列化为字符串,作为参数传递给这个调用的方法: var result = `${cbName}(${JSON.stringify(data)})`; // 将拼接好的方法的调用,返回给客户端去解析执行 res.end(result); } else { res.end('404'); } }); server.listen(3000, () => { console.log('server running at http://127.0.0.1:3000'); });
直接在页面中,通过script标签,引入 vue-resource 的脚本文件
引用的先后顺序是:先引用 Vue 的脚本文件,再引用 vue-resource 的脚本文件
get一个参数,要请求的URL地址
post三个参数
参数一,要请求的URL地址
参数二,要发送的数据对象
参数三,指定post提交的编码类型为application/x-www-form-urlencoded
jsonp一个参数,要请求的URL地址
then后面是成功之后的函数
//发送get getInfo() { // get 方式获取数据 this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => { console.log(res.body); }) } //发送post postInfo() { //将post发送请求的第一个参数提取出来 var url = 'http://127.0.0.1:8899/api/post'; //要发送的数据是 name:'zs' //指定post提交编码的类型为application/x-www-form-urlencoded this.$http.post(url, { name: 'zs' }, { emulateJSON: true }).then(res => { console.log(res.body); }); } //发送jsonp jsonpInfo() { // JSONP形式从服务器获取数据 var url = 'http://127.0.0.1:8899/api/jsonp'; this.$http.jsonp(url).then(res => { console.log(res.body); }); }
地址中如果有相同的根路径,可以提取出来
请求中,url前面有斜杠的时候,不启用根路径的设置,没有斜杠的时候,启用根路径拼接
Vue.http.options.root='http://vue.studyit.io' //api前面没有斜杠,表示要和前面抽离的根路径部分进行拼接,形成完整的路径 this.$http.get('api/getprodlist').then(result=>{//功能})
和根路径类似的,post中的第三个参数也可以提取出来,进行全局配置
指定post提交的编码类型为application/x-www-form-urlencoded
Vue.http.options.emulateJSON=true;
先解压安装 PHPStudy;
解压安装 Navicat 这个数据库可视化工具,并激活;
打开 Navicat 工具,新建空白数据库,名为 dtcmsdb4;
双击新建的数据库,连接上这个空白数据库,在新建的数据库上右键 -> 运行SQL文件,选择并执行dtcmsdb4.sql 这个数据库脚本文件;如果执行不报错,则数据库导入完成;
进入文件夹 vuecms3_nodejsapi 内部,执行 npm i 安装所有的依赖项;
先确保本机安装了 nodemon, 没有安装,则运行 npm i nodemon -g 进行全局安装,安装完毕后,进入到 vuecms3_nodejsapi目录 -> src目录 -> 双击运行 start.bat
如果API启动失败,请检查 PHPStudy 是否正常开启,同时,检查 app.js 中第 14行 中数据库连接配置字符串是否正确;PHPStudy 中默认的 用户名是root,默认的密码也是root
动画的一个过程包括四个时间点和两个时间段
使用transition元素把需要被动画控制的元素包裹起来
transition是Vue官方提供的
Vue把一个完整的动画,使用钩子函数,拆分为了两部分
使用flag标识符来表示动画的切换
两组:v-enter,v-leave-to和v-enter-active,v-leave-active
style标签中设置两组样式
v-enter 时间点,进入之前,元素的起始状态,此时还没有开始进入
v-leave-to 动画离开之后,离开的终止状态,此时,元素动画已经结束
v-enter-active 入场动画的时间段
v-leave-active 出场动画的时间段
区分不同组之间的动画,在transition上加一个name
可以利用:duration="毫秒值" 来统一设置时间
... <style> .v-enter, .v-leave-to { opacity: 0; transform: translateX(150px); } .v-enter-active, .v-leave-active{ transition: all 0.8s ease; } style> head> <body> <div id="app"> <input type="button" value="toggle" @click="flag=!flag"> <transition> <h3 v-if="flag">这是一个H3h3> transition> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { //flag初始值设定为false flag: false }, methods: {} }); script> body> html> ----------------动画部分可以添加name属性,这样类样式也要跟着改变,将前缀改成name的值 <transition name="my"> <h6 v-if="flag2">这是一个H6h6> transition> <style> .my-enter, .my-leave-to { opacity: 0; transform: translateY(70px); } .my-enter-active, .my-leave-active{ transition: all 0.8s ease; } style>
transition中enter-active-class="bounceIn"和leave-active-class="bounceOut"属于第三方类,可以用来设置特殊的效果
:duration="{ enter: 200, leave: 400 }"可以分开设置时间
除了效果的类名,还需要加animated类,这个类可以加到transition上也可以加到元素上
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Documenttitle> <script src="./lib/vue-2.4.0.js">script> <link rel="stylesheet" href="./lib/animate.css"> head> <body> <div id="app"> <input type="button" value="toggle" @click="flag=!flag"> <transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="{ enter: 200, leave: 400 }"> <h3 v-if="flag" class="animated">这是一个H3h3> transition> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { flag: false }, methods: {} }); script> body> html>
使用transition把元素包裹起来
Vue把一个完整的动画,使用钩子函数,拆分为了两部分
动画钩子函数的第一个参数:el,表示要执行动画的那个DOM元素,是个原生的 JS DOM对象,在下面的例子中,el就是小球
el 是通过document.getElementById('') 方式获取到的原生JS DOM对象
beforeEnter 表示动画入场之前动画尚未开始,可以在beforeEnter 中,设置元素开始动画之前的起始样式
enter 表示动画开始之后的样式,可以设置小球完成动画之后的,结束状态
enter中el.offsetWidth这句话没有实际的作用,但是如果不写会报错
可以认为 el.offsetWidth 会强制动画刷新,这里的Width可以写成Height,Left,Right
enter中的第二个参数done,之后在enter函数最后调用了done
这里的 done, 起始就是 afterEnter 这个函数,也就是说,done 是 afterEnter 函数的引用
下面的例子是半场动画
再次点击按钮的时候 小球瞬间回到00位置,接着执行之前的过程,这就是为什么每次点击小球都会回到原处的原因
... <style> .ball { width: 15px; height: 15px; border-radius: 50%; background-color: red; } style> head> <body> <div id="app"> <input type="button" value="快到碗里来" @click="flag=!flag"> <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"> <div class="ball" v-show="flag">div> transition> div> <script> // 创建 Vue 实例 var vm = new Vue({ el: '#app', data: { flag: false }, methods: { beforeEnter(el){ // 设置小球开始动画之前的,起始位置 el.style.transform = "translate(0, 0)" }, enter(el, done){ el.offsetWidth el.style.transform = "translate(150px, 450px)" el.style.transition = 'all 1s ease' //这里调用done就相当于调用afterEnter done() }, afterEnter(el){ // 动画完成之后,会调用 afterEnter // console.log('ok') this.flag = !this.flag } } }); script> body> html>
v-move和v-leave-active配合使用,能够实现列表后续的元素,渐渐浮上来的效果
列表过渡的时候,不能再用transition包裹,需要用transition-group
为v-for循环创建的元素设置动画,必须为每一个元素设置:key属性
给transition-group添加appear属性,实现页面刚展示出来的时候,入场时候的效果
通过为transition-group设置tag属性,指定transition-group渲染之后成为什么元素,如果不指定tag属性,默认渲染span标签,如果是在ul标签中默认渲染,ul标签中除了li还有span,不符合W3C语法标准,所以这里要把ul标签去掉,让transition-group渲染成ul
... <style> li { border: 1px dashed #999; margin: 5px; line-height: 35px; padding-left: 5px; font-size: 12px; width: 100%; } li:hover { background-color: hotpink; transition: all 0.8s ease; } .v-enter, .v-leave-to { opacity: 0; transform: translateY(80px); } .v-enter-active, .v-leave-active { transition: all 0.6s ease; } .v-move { transition: all 0.6s ease; } .v-leave-active{ position: absolute; } style> head> <body> <div id="app"> <div> <label>Id: <input type="text" v-model="id"> label> <label>Name: <input type="text" v-model="name"> label> <input type="button" value="添加" @click="add"> div> <transition-group appear tag="ul"> <li v-for="(item, i) in list" :key="item.id" @click="del(i)"> {{item.id}} --- {{item.name}} li> transition-group> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { id: '', name: '', list: [ { id: 1, name: '赵高' }, { id: 2, name: '秦桧' }, { id: 3, name: '严嵩' }, { id: 4, name: '魏忠贤' } ] }, methods: { add() { //list中添加元素 this.list.push({ id: this.id, name: this.name }) this.id = this.name = '' }, del(i) { //删除当前项 this.list.splice(i, 1) } } }); script> body> html>
组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可。
组件化和模块化的不同:
模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一
组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用
Vue实例就是父组件
path设置地址,component设置组件,router-to表示去哪个路由
第一种
使用Vue.extend来创建全局的Vue组件
使用Vue.component('组件名称',创建出来的组件模板对象)
template属性制定了组件要展示的HTML结构
如果使用Vue.component定义全局组件的时候,组件名称使用了驼峰命名,在引用组件的时候,就要用短横连接的方式。如果没有使用短横连接,直接写这个名字就可以
Vue.component第一个参数是组件名称,在引用组件的时候,以一个标签形式引入,第二个参数是Vue.extend创建的组件,其中template就是组件将要展示的HTML内容
<body> <div id="app"> <mycom1>mycom1> div> <script> //创建全局的Vue组件 var com1 = Vue.extend({ template: '这是使用 Vue.extend 创建的组件
' }) //定义全局组件 Vue.component('mycom1', com1) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {} }); script> body> html> ------- //可以简写成下面这种模式,不需要中间变量来接收 <script> Vue.component('mycom1', Vue.extend({ template: '这是使用 Vue.extend 创建的组件
' })) script>
第二种
无论哪种方式创建出来的组件,组件的template属性指向的模板内容,必须有且只有唯一一个的根元素
如果不加div标签在最外边包裹,h3和span并列使用,并且是根元素的位置,这样会报错
<body> <div id="app"> <mycom2>mycom2> div> <script> //这里省去了extend,直接引用模板 Vue.component('mycom2', { template: '' }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {} }); script> body> html>这是直接使用 Vue.component 创建出来的组件
123
第三种
在被控制的#app外面,使用template元素,定义组件的HTML模板结构
实例内部添加components对象,可以定义私有的组件
<body> <div id="app"> <mycom3>mycom3> div> <div id="app2"> <mycom3>mycom3> <login>login> div> <template id="tmpl"> <div> <h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的只能提示和高亮h1> <h4>好用,不错!h4> div> template> <template id="tmpl2"> <h1>这是私有的 login 组件h1> template> <script> //全局的组件 Vue.component('mycom3', { template: '#tmpl' }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {} }); var vm2 = new Vue({ el: '#app2', data: {}, methods: {}, filters: {}, directives: {}, components: { // 定义实例内部私有组件的 login: { template: '#tmpl2' } } }) script> body> html> -------- 如果没有使用模板,并且是局部私有组件 <script> components: { // 定义实例内部私有组件的 'login':login } //这里属性名和值一样,可以进一步简写 components: { // 定义实例内部私有组件的 login } script>
组件中可以有自己的data数据
组件的data和实例的data不一样,实例中的data可以是对象,但是组件中的data必须是一个方法
组件中data的内部必须返回一个对象
组件中data数据使用方式和实例中data使用方式相同
<body> <div id="app"> <mycom1>mycom1> div> <script> Vue.component('mycom1', { template: '这是全局组件 --- {{msg}}
', //组件中data必须是方法 data: function () { //data中必须返回一个对象 return { msg: '这是组件的中data定义的数据' } } }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {} }); script> body> html>
为什么data内部必须return一个对象
如果当count的数据在全局定义成一个对象的时候,在data中直接引用,相当于是引用了全局的对象
这个对象是引用类型,一个发生变化,所有的都会发生变化
所以对象需要data内部自己定义
<body> <div id="app"> <counter>counter> <hr> <counter>counter> <hr> <counter>counter> div> <template id="tmpl"> <div> <input type="button" value="+1" @click="increment"> <h3>{{count}}h3> div> template> <script> var dataObj = { count: 0 } // 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1 Vue.component('counter', { template: '#tmpl', data: function () { //如果返回dataObj,上面的counter,三个都会是同一个值,因为是引用类型 // return dataObj return { count: 0 } }, methods: { increment() { this.count++ } } }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {} }); script> body> html>
第一种
<body> <div id="app"> <a href="" @click.prevent="flag=true">登录a> <a href="" @click.prevent="flag=false">注册a> <login v-if="flag">login> <register v-else="flag">register> div> <script> Vue.component('login', { template: '登录组件
' }) Vue.component('register', { template: '注册组件
' }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { flag: false }, methods: {} }); script> body> html>
第二种
Vue提供了component元素,展示对应的名称组件,相当于一个占位符
冒号is属性,可以用来指定要展示的组件的名称
冒号表示绑定,双引号中的内容默认不是字符串,所以组件名称(字符串)放进去的时候要加单引号
可以在data中定义一个变量,用来承载组件名称,点击链接切换这个变量的值就可以了
组件也可以用动画的标签包裹起来,加一个属性先出后进
<body> <div id="app"> <a href="" @click.prevent="comName='login'">登录a> <a href="" @click.prevent="comName='register'">注册a> <component :is="comName">component> div> <script> // 组件名称是 字符串 Vue.component('login', { template: '登录组件
' }) Vue.component('register', { template: '注册组件
' }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { // 当前 component 中的 :is 绑定的组件的名称 // 默认是login comName: 'login' }, methods: {} }); script> body> html>
切换动画
... <style> .v-enter, .v-leave-to { opacity: 0; transform: translateX(150px); } .v-enter-active, .v-leave-active { transition: all 0.5s ease; } style> head> <body> <div id="app"> <a href="" @click.prevent="comName='login'">登录a> <a href="" @click.prevent="comName='register'">注册a> <transition mode="out-in"> <component :is="comName">component> transition> div> <script> // 组件名称是 字符串 Vue.component('login', { template: '登录组件
' }) Vue.component('register', { template: '注册组件
' }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { comName: 'login' // 当前 component 中的 :is 绑定的组件的名称 }, methods: {} }); script> body> html>
父组件,可以在引用子组件的时候, 通过属性绑定(v-bind:)的形式,把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用
子组件如果想使用这个属性需要在自己内部用props定义一下
这里的props是一个数组,里面放字符串格式的属性名就可以了
子组件中,默认无法访问到父组件中的data上的数据和methods中的方法
子组件中的data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上
data 上的数据,都是可读可写的。props 中的数据,都是只读的,无法重新赋值
<body> <div id="app"> <com1 v-bind:parentmsg="msg">com1> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { msg: '123 父组件中的数据' }, methods: {}, components: { //子组件 com1: { //子组件中data是一个function,return的是一个对象 data() { return { title: '123', content: 'qqq' } }, //子组件中定义模板 template: '这是子组件---{{parentmsg}}
', //子组件的props定义父组件中的属性 props: ['parentmsg'], methods: { change() { this.parentmsg = '被修改了' } } }, } }); script> body>
父组件向子组件传递方法,使用的是事件绑定机制
v-on当我们自定义了一个事件属性之后,子组件就能够,通过某些方式,来调用传递进去的这个方法了
show不带括号,表示直接将方法引用给了func
如果有括号,表示将show的return结果给func
emit代表触发的意思
点击组件上的按钮就能调用父组件上的方法
如果父组件中的方法有参数,在子组件中引用的时候,从第二个参数开始就可以传递参数
<body> <div id="app"> <com2 @func="show">com2> div> <template id="tmpl"> <div> <h1>这是子组件h1> <input type="button" value="子组件按钮,点击触发父组件传过来的方法" @click="myclick"> div> template> <script> var com2 = { template: '#tmpl', data() { return { sonmsg: { name: '小头儿子', age: 8 } } }, methods: { myclick() { this.$emit('func', this.sonmsg) } } } // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { datamsgFormSon: null }, methods: { show(data) { this.datamsgFormSon = data console.log('调用了父组件身上的 show 方法: --- ' + data) } }, components: { com2 } }); script> body>
父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去
父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称
<son @func="getMsg">son>
子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
<div id="app"> <son @func="getMsg">son> <script type="x-template" id="son">script> div> <script> // 子组件的定义方式 Vue.component('son', { template: '#son', // 组件模板Id methods: { sendMsg() { // 按钮的点击事件 this.$emit('func', 'OK'); // 调用父组件传递过来的方法,同时把数据传递出去 } } }); // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: { getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义 alert(val); } } }); script>
可以通过ref获取元素,reference引用的意思
h3就是单个的元素ref,不需要带s,但是vm上的$refs可以有多个引用,所以带s
通过$refs可以拿到组件的引用,可以直接用组件中的数据和方法
<body> <div id="app"> <input type="button" value="获取元素" @click="getElement" ref="mybtn"> <h3 id="myh3" ref="myh3">哈哈哈哈h3> <hr> <login ref="mylogin">login> div> <script> var login={ template:'登陆组件
', data(){ return{ msg:'son msg' } }, methods:{ show(){ console.log('调用了子组件的方法'); } } } // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: { getElement(){ console.log(this.$refs.mylogin.msg); } }, components: { //这里将login抽离出去进行定义了 login } }); script> body>
后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源。
前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现。
在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由)。
导入router的包
使用router-link组件来导航
使用router-view 组件来显示匹配到的组件,相当于占位符
创建一个路由router 实例,通过routes属性来定义路由匹配规则,注意这里是routes不是router
使用router属性来使用路由规则
router通过vm实例的一个属性和vm关联起来
其中router-link的地方可以用a标签实现跳转,但是使用hash每次都要加#,<a href="#/login">登录</a>,这么写比较麻烦,所以采用router-link的写法
router-link会默认渲染成一个a标签,里面添加tag属性,可以指定渲染成什么标签,但是不管渲染成什么标签依然有跳转的功能
redirect是重定向
router中的链接有特殊的类名,给这些类名添加样式就可以控制链接的高亮效果
特殊类名是router-link-active,是默认的,可以使用linkActiveClass更改成简单的名字
将路由规则对象,注册到vm实例上,用来监听url地址的变化,然后展示对应的组件
<body> <div id="app"> <router-link to="/login" tag="span">登录router-link> <router-link to="/register">注册router-link> <transition> <router-view>router-view> transition> div> <script> var login = { template: '登录组件
' } var register = { template: '注册组件
' } //创建路由对象 var routerObj = new VueRouter({ //routes分发路由 routes: [ //redirect重定向,根路径的时候,重定向到login页面 {path: '/',redirect: '/login'}, {path: '/login',component: login}, {path: '/register',component: register} ], //将复杂的路由类名改成自己定义的类名 linkActiveClass: 'myactive' }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {}, //用来监听url地址变化 router: routerObj }); script> body>
如果在路由中,使用查询字符串,给路由传递参数,则不需要修改 路由规则的path属性
可以直接在模板中渲染这个参数
用query获取参数
<body> <div id="app"> <router-link to="/login?id=10&name=zs">登录router-link> <router-link to="/register">注册router-link> <router-view>router-view> div> <script> var login = { template: '登录-{{$route.query.id}}-{{$route.query.name}}
', data() { return { msg: '123' } }, created() { console.log(this.$route); } } var register = { template: '注册
' } var router = new VueRouter({ routes: [{ path: '/login', component: login }, { path: '/register', component: register }, ] }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {}, router }); script> body>
可以使用另一种方法
可以直接从params中获取参数
params是通过match解析出来的
<body> <div id="app"> <router-link to="/login/12/ls">登录router-link> <router-link to="/register">注册router-link> <router-view>router-view> div> <script> var login = { template: '登录 --- {{ $route.params.id }} --- {{ $route.params.name }}
', data(){ return { msg: '123' } }, created(){ // 组件的生命周期钩子函数 console.log(this.$route.params.id) } } var register = { template: '注册
' } var router = new VueRouter({ routes: [ { path: '/login/:id/:name', component: login }, { path: '/register', component: register } ] }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {}, // router: router router }); script> body>
使用children属性,实现子路由,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
<body> <div id="app"> <router-link to="/account">Accountrouter-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> // 组件的模板对象 var account = { template: '#tmpl' } var login = { template: '登录
' } var register = { template: '注册
' } var router = new VueRouter({ routes: [ { path: '/account', component: account, // children: [ { path: 'login', component: login }, { path: 'register', component: register } ] } ] }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {}, router }); script> body>
router-view有name属性可以匹配到相应的内容
<body> <div id="app"> <router-view>router-view> <div class="container"> <router-view name="left">router-view> <router-view name="main">router-view> div> div> <script> var header = { template: 'Header头部区域
' } var leftBox = { template: 'Left侧边栏区域
' } var mainBox = { template: 'mainBox主体区域
' } // 创建路由对象 var router = new VueRouter({ routes: [ /* { path: '/', component: header }, { path: '/left', component: leftBox }, { path: '/main', component: mainBox } */ { path: '/', components: { 'default': header, 'left': leftBox, 'main': mainBox } } ] }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {}, router }); script> body>
文本框绑定键盘抬起事件
这种方式可以监测文本框等可以绑定事件的元素,但如果想要监测地址栏的内容,用这种绑定方法就不能执行了
<body> <div id="app"> <input type="text" v-model="firstname" @keyup="getFullname"> + <input type="text" v-model="lastname" @keyup="getFullname"> = <input type="text" v-model="fullname"> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { firstname: '', lastname: '', fullname: '' }, methods: { getFullname() { this.fullname = this.firstname + '-' + this.lastname } } }); script> body>
和methods同级的,有一个watch对象
watch中firstname属性可以监视data中指定数据的变化,然后触发watch中对应的function处理函数
这个函数中可以监测新值旧值的变化,newVal和oldVal
<body> <div id="app"> <input type="text" v-model="firstname"> + <input type="text" v-model="lastname"> = <input type="text" v-model="fullname"> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { firstname: '', lastname: '', fullname: '' }, methods: {}, watch: { // 使用这个 属性,可以监视 data 中指定数据的变化, //然后触发这个 watch 中对应的 function 处理函数 'firstname': function (newVal, oldVal) { console.log('监视到了 firstname 的变化') // this.fullname = this.firstname + '-' + this.lastname // console.log(newVal + ' --- ' + oldVal) this.fullname = newVal + '-' + this.lastname }, 'lastname': function (newVal) { this.fullname = this.firstname + '-' + newVal } } }); script> body>
watch监测路由地址的变化
将$route.path传到watch里当做属性
如果newVal是/login表明跳转到了登录页面
如果newVal是/register表明跳转到了注册页面
<body> <div id="app"> <router-link to="/login">登录router-link> <router-link to="/register">注册router-link> <router-view>router-view> div> <script> // 2. 创建子组件 var login = { template: '这是登录子组件,这个组件是 奔波霸 开发的。
' } var register = { template: '这是注册子组件,这个组件是 霸波奔 开发的。
' } // 3. 创建一个路由对象 var router = new VueRouter({ routes: [ // 路由规则数组 { path: '/', redirect: '/login' }, { path: '/login', component: login }, { path: '/register', component: register } ], linkActiveClass: 'myactive' // 和激活相关的类 }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: {}, methods: {}, // router: router router, watch: { // this.$route.path '$route.path': function (newVal, oldVal) { // console.log(newVal + ' --- ' + oldVal) if (newVal === '/login') { console.log('欢迎进入登录页面') } else if (newVal === '/register') { console.log('欢迎进入注册页面') } } } }); script> body>
computed中定义的属性叫做计算属性
计算属性的本质就是一个方法
在使用计算属性的时候,将这些名称直接当做属性来使用,并不会把计算属性当做方法调用
引用的时候,一定不要加括号去调用,直接当做普通属性去使用就可以了
计算属性的求值结果会被缓存起来,方便下次直接使用,如果计算属性方法所有数据都没有发生过变化,不会重新对属性求值
输出fullname三次,但是只触发了一次
computed始终都要return出去一个值
<body> <div id="app"> <input type="text" v-model="firstname"> + <input type="text" v-model="middlename"> + <input type="text" v-model="lastname"> = <input type="text" v-model="fullname"> <p>{{ fullname }}p> <p>{{ fullname }}p> <p>{{ fullname }}p> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { firstname: '', lastname: '', middlename: '' }, methods: {}, computed: { 'fullname': function () { console.log('ok') return this.firstname + '-' + this.middlename + '-' + this.lastname } } }); script> body>
默认只有getter的计算属性
<div id="app"> <input type="text" v-model="firstName"> + <input type="text" v-model="lastName"> = <span>{{fullName}}span> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { firstName: 'jack', lastName: 'chen' }, methods: {}, computed: { // 计算属性; 特点:当计算属性中所以来的任何一个 data 属性改变之后,都会重新触发 本计算属性 的重新计算,从而更新 fullName 的值 fullName() { return this.firstName + ' - ' + this.lastName; } } }); script>
定义有getter和setter的计算属性
<div id="app"> <input type="text" v-model="firstName"> <input type="text" v-model="lastName"> <input type="button" value="修改fullName" @click="changeName"> <span>{{fullName}}span> div> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { firstName: 'jack', lastName: 'chen' }, methods: { changeName() { this.fullName = 'TOM - chen2'; } }, computed: { fullName: { get: function () { return this.firstName + ' - ' + this.lastName; }, set: function (newVal) { var parts = newVal.split(' - '); this.firstName = parts[0]; this.lastName = parts[1]; } } } }); script>
computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用。
methods方法表示一个具体的操作,主要书写业务逻辑。
watch一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed和methods的结合体。
提供了一些最常用的NPM包镜像地址,能够让我们快速的切换安装包时候的服务器地址。
原来包刚一开始是只存在于国外的NPM服务器,但是由于网络原因,经常访问不到,这时候,我们可以在国内,创建一个和官网完全一样的NPM服务器,只不过,数据都是从人家那里拿过来的,除此之外,使用方式完全一样。
安装运行
运行npm i nrm -g全局安装nrm包
使用nrm ls查看当前所有可用的镜像源地址以及当前所使用的镜像源地址
使用nrm use npm或nrm use taobao切换不同的镜像源地址
这里默认是用npm装包
切换用use 指令
nrm中的cnpm和cnpm性质不同,前者就是一个地址,后者是一个工具
普通页面中使用vue
使用script标签,引入vue包
在index页面中,创建一个id为app的div容器
通过new Vue得到一个vm实例
在webpack中使用import Vue from ‘vue’导入的Vue构造函数,功能不完整,只提供了runtime-only的方式,并没有提供像网页中那样的使用方式
这里可以将from后面引号中的内容修改成vue.js文件的路径,也就是通过script引入的那个文件,这么做相当于是用script引入了js文件
或者修改package.json文件中,main属性的值,入口文件由index.js改成vue.js
或者设置vue被导入时候包的路径,在webpack.config.js中和modules同级地建一个对象
resolve:{ alias:{ "vue$": "vue/dist/vue.js" } } //这种方法的import导入的是'vue',不是vue.js
vue结合webpack的时候,只能用render的方式进行渲染页面
render: function (createElements) { return createElements(login) } //可以用箭头函数表示,只有一个参数,可以省去括号,参数可以简写。只有一条return语句,可以省去return和花括号 render:c=>c(login)
导出
在login.vue中用export default方式导出节点
node向外暴露成员的方式model.exports={}
ES6中,也通过规范的形式,规定了ES6中如何导入和导出模块
ES6中,导入模块使用import,导入成员使用exports default
导入,导出不能混用,最好统一使用
向外暴露成员的时候,可以使用任意的变量来接收
使用export default暴露,只能暴露一次,否则会报错
组件中的data必须是function
export default { data() { // 注意:组件中的 data 必须是 function return { msg: "123" }; }, methods: { show() { console.log("调用了 login.vue 中的 show 方法"); } } };
exports暴露出去的成员,需要用花括号来接收,这种形式叫做按需导出
export var title='小星星' --- import m222,{title} from './test.js'
exports可以向外暴露多个成员
在import的时候不需要 可以不在花括号中定义,这就叫做按需导出
使用exports导出的成员必须严格按照导出时候的名称来使用花括号按需接收
一定要用别的名字,这里使用as
import m222,{title as title123,content} from './test.js'
安装vue的包:cnpm i vue -S
安装loader文件:cnpm i vue-template-complier -D
在main中导入vue模块,import Vue from ‘vue’
新建一个纯粹的组件文件login.vue,由template、script、style组成
main.js中导入login文件,import login from ‘./login.vue’
创建vm实例var vm=new Vue({el:'#app',render:c=>c(login)})
页面中创建一个id为app的div元素,作为我们vm实例要控制的区域
main.js中
render会把el指定的容器中,所有的内容都清空覆盖,所以不要把路由的router-view和router-link直接写到el所控制的元素中
App 这个组件,是通过VM实例的render函数,渲染出来的,render函数如果要渲染组件,渲染出来的组件,只能放到 el: '#app' 所指定的元素中
Account和GoodsList组件,是通过路由匹配监听到的,这两个组件,只能展示到属于路由的<router-view></router-view> 中去。
//导入vue模块 import Vue from 'vue' //导入vue-router包 import VueRouter from 'vue-router' //手动安装VueRouter Vue.use(VueRouter) // 导入 app 组件,父组件 import app from './App.vue' // 导入 Account 组件,子组件 import account from './main/Account.vue' import goodslist from './main/GoodsList.vue' //创建路由对象 var router = new VueRouter({ routes: [ // account goodslist { path: '/account', component: account }, { path: '/goodslist', component: goodslist } ] }) var vm = new Vue({ el: '#app', render: c => c(app), // render 会把 el 指定的容器中,所有的内容都清空覆盖,所以 不要 把 路由的 router-view 和 router-link 直接写到 el 所控制的元素中 router // 4. 将路由对象挂载到 vm 上 })
webpack.config.js
由于webpack是基于Node进行构建的,webpack的配置文件中,任何合法的Node代码都是支持的
limit 给定的值,是图片的大小,单位是 byte,如果我们引用的图片,大于或等于给定的 limit值,则不会被转为base64格式的字符串, 如果图片小于给定的 limit 值,则会被转为base64的字符串
var path = require('path') // 在内存中,根据指定的模板页面,生成一份内存中的首页,同时自动把打包好的bundle注入到页面底部 // 如果要配置插件,需要在导出的对象中,挂载一个 plugins 节点 var htmlWebpackPlugin = require('html-webpack-plugin') // 当以命令行形式运行 webpack 或 webpack-dev-server 的时候,工具会发现,我们并没有提供 要打包 的文件的 入口 和 出口文件,此时,他会检查项目根目录中的配置文件,并读取这个文件,就拿到了导出的这个 配置对象,然后根据这个对象,进行打包构建 module.exports = { entry: path.join(__dirname, './src/main.js'), // 入口文件 output: { // 指定输出选项 path: path.join(__dirname, './dist'), // 输出路径 filename: 'bundle.js' // 指定输出文件的名称 }, plugins: [ // 所有webpack 插件的配置节点 new htmlWebpackPlugin({ template: path.join(__dirname, './src/index.html'), // 指定模板文件路径 filename: 'index.html' // 设置生成的内存页面的名称 }) ], module: { // 配置所有第三方loader 模块的 rules: [ // 第三方模块的匹配规则 // 处理 CSS 文件的 loader { test: /\.css$/, use: ['style-loader', 'css-loader'] }, // 处理 less 文件的 loader { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, // 处理 scss 文件的 loader { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }, // 处理 图片路径的 loader { test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=7631&name=[hash:8]-[name].[ext]' }, // 处理 字体文件的 loader { test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }, // 配置 Babel 来转换高级的ES语法 { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, // 处理 .vue 文件的 loader { test: /\.vue$/, use: 'vue-loader' } ] }, resolve: { alias: { // 修改 Vue 被导入时候的包的路径 // "vue$": "vue/dist/vue.js" } } }
vue模块可以实现嵌套
将main模块中路由分发内容抽离到router中
main.js
import Vue from 'vue' // 1. 导入 vue-router 包 import VueRouter from 'vue-router' // 2. 手动安装 VueRouter Vue.use(VueRouter) // 导入 app 组件 import app from './App.vue' // 导入 自定义路由模块 import router from './router.js' var vm = new Vue({ el: '#app', render: c => c(app), router // 4. 将路由对象挂载到 vm 上 })
router.js
import VueRouter from 'vue-router' // 导入 Account 组件 import account from './main/Account.vue' import goodslist from './main/GoodsList.vue' // 导入Account的两个子组件 import login from './subcom/login.vue' import register from './subcom/register.vue' // 3. 创建路由对象 var router = new VueRouter({ routes: [ // account goodslist { path: '/account', component: account, children: [ { path: 'login', component: login }, { path: 'register', component: register } ] }, { path: '/goodslist', component: goodslist } ] }) // 把路由对象暴露出去 export default router
普通的style标签只能写CSS样式
style标签有lang属性,可以让lang属性值为scss,这样就可以识别scss样式
只要style标签是vue文件中定义的,需要加上scoped属性,scope解决样式的作用域问题
只要为组件中style添加scope,就会对最外层的div添加一个自定义属性,这个自定义属性用来设置样式,通过CSS的属性选择器实现的
使用export default和export导出模块中的成员,对应ES5中的module.exports和export
使用import ** from ** 和import '路径' 还有 import {a, b} from '模块标识' 导入其他模块
使用箭头函数:(a, b)=> { return a-b; }