Vue
在我们的大型项目开发的时候我们往往都会采用前后端分离的方案,而我们的前端一般都是采用Vue框架来实现前后端分离。那这样我们就不能不讲一下Vue开发的优点了:
- 先进的前端设计模式:MVVM
- 可以完全脱离服务器端,以 前端代码复用的方式渲染整个页面:组件化开发
特点
- 单页面Web应用
- 数据驱动
- 数据的双先绑定
- 虚拟DOM
平时开发可以直接使用js文件导入的形式使用Vue开发
<div id="app"> {{ }} </div> <script src="js/vue.min.js"></script> <script> new Vue({ el: '#app' }) </script>
实例成员
el
实例与页面挂载点一一对应
一个页面中可以出现多个实例对应多个挂载点
实例只操作挂载点内部内容
data
data为插值表达式中的变量提供数据
data中的数据可以通过Vue实例直接或间接访问
methods
- methods为事件提供实现体
computed
- 一个变量依赖于多个变量
- 当依赖的变量发生变化,便会触发
watch:监听
- 用于监听某个变量的变化
delimiters:分隔符
- 当我们的
{{}}
与其他组件发生冲突时可以使用这个来替换新的标识符
- 当我们的
指令
文本指令(数据需要在Vue中定义)
插值表达式
<p>{{ msg }}</p>
v-text(替换原文本)
<p v-text='msg'>原文本</p>
v-html(替换原文本,并且解析html标签)
v-once(只渲染一次)
斗篷指令
<div id="app"> <p>{{ msg }}</p> <p>{{ msg }}</p> <p>{{ msg }}</p> <p>{{ msg }}</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="text/javascript"> new Vue({ el: "#app", data: { msg: "message" } }) </script>
在这种情况下回发生一个问题就是页面是先加载后渲染数据的,所以你可以看到先是一堆的
{{ msg }}
然后才是渲染的数据。解决这个办法有两个方法将导入Vue.js的语句放在head里
<html> <head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <p>{{ msg }}</p> <p>{{ msg }}</p> <p>{{ msg }}</p> <p>{{ msg }}</p> </div> </body> <script type="text/javascript"> new Vue({ el: "#app", data: { msg: "message" } }) </script> </html>
通过斗篷指令
<html> <head> <style type="text/css"> [v-cloak] { display: none; } </style> </head> <body> <div id="app" v-cloak> <p>{{ msg }}</p> <p>{{ msg }}</p> <p>{{ msg }}</p> <p>{{ msg }}</p> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="text/javascript"> new Vue({ el: "#app", data: { msg: "message" } }) </script> </html>
属性指令(属性绑定变量)
绑定属性
<p v-bind:abc="abc"></p>
绑定类
单类名绑定
<p v-bind:class="c1"></p>
多类名绑定
<p v-bind:class="[c2, c3]"></p>
控制每个类样式
{c4: true|false|var}
绑定样式
<div :style="div_style"></div>
<div :style="{width: '100px', height: '100px', backgroundColor: 'blue'}"></div>
事件指令(绑定事件)
v-on: 指令 简写 @ 不传参事件绑定,但事件回调方法可以获取事件对象 <p @click="fn"></p> 参数可以通过传入具体实参 <p @click="fn()"></p> 方法函数需要参数并没有传入的情况下,事件对象默认传入(事件对象:$event) <p @click="fn()"></p>
表单指令
<div id="app"> <!-- v-model针对于表单元素 --> <form action="" method="get"> <!-- 1、双向绑定:服务于文本输入框 --> <!-- v-model存储的值为输入框的value值 --> <div> <input type="text" name="usr" v-model="in_val"> <input type="password" name="ps" v-model="in_val" > <textarea name="info" v-model="in_val"></textarea> </div> <!-- 2、单选框 --> <div> <!-- 单选框是以name进行分组,同组中只能发生单选 --> <!-- v-model存储的值为单选框的value值 --> 男:<input type="radio" name="sex" value="男" v-model="ra_val"> 女:<input type="radio" name="sex" value="女" v-model="ra_val"> {{ ra_val }} </div> <!-- 3、单一复选框 --> <!-- v-model存储的值为true|false --> <!-- 或者为自定义替换的值 --> <div> <input type="checkbox" v-model='sin_val' true-value="选中" false-value="未选中" /> {{ sin_val }} </div> <!-- 4、多复选框 --> <!-- v-model存储的值为存储值多复选框value的数组 --> <div> <input type="checkbox" value="喜好男的" name="cless" v-model='more_val' /> <input type="checkbox" value="喜好女的" name="cless" v-model='more_val' /> <input type="checkbox" value="不挑" name="cless" v-model='more_val' /> {{ more_val }} </div> </form> </div> <script type="text/javascript"> new Vue({ el: '#app', data: { in_val: '', // 默认值可以决定单选框默认选项 ra_val: '男', // 默认值为true,单一复选框为选中,反之false为不选中 sin_val: '', // 数组中存在的值对应的复选框默认为选中状态 more_val: ['喜好女的','不挑'] } }) </script>
条件指令
<div id="app"> <button @click="toggle">显隐切换</button> <!-- v-if --> <div class="box r" v-if="isShow"></div> <!-- v-show --> <div class="box o" v-show="isShow"></div> <!-- 1.条件渲染的值为true|false --> <!-- 2.true代表标签显示方式渲染 --> <!-- 3.false v-if不渲染到页面,v-show以display:none渲染到页面,但也不会显示 --> <!-- v-if v-else-if v-else 案例 --> <ul> <li @mouseover="changeWrap(0)">red</li> <li @mouseover="changeWrap(1)">green</li> <li @mouseover="changeWrap(2)">blue</li> </ul> <!-- red页面逻辑结构 --> <div class="wrap red" v-if="tag == 0" key="0">...</div> <!-- green页面逻辑结构 --> <div class="wrap green" v-else-if="tag == 1" key="1">...</div> <!-- blue页面逻辑结构 --> <div class="wrap blue" v-else key="2">...</div> <!-- v-if相关分支操作,在未显示情况下,是不会被渲染到页面中 --> <!-- 通过key全局属性操作后,渲染过的分支会建立key对应的缓存,提高下一次渲染速度 --> <!-- v-show 案例 --> <ul> <li @mouseover="changeMain(0)">red</li> <li @mouseover="changeMain(1)">green</li> <li @mouseover="changeMain(2)">blue</li> </ul> <!-- red页面逻辑结构 --> <div class="main red" v-show="whoShow(0)">...</div> <!-- green页面逻辑结构 --> <div class="main green" v-show="whoShow(1)">...</div> <!-- blue页面逻辑结构 --> <div class="main blue" v-show="whoShow(2)">...</div> </div> <script type="text/javascript"> new Vue({ el: "#app", data: { isShow: false, tag: 0, flag: 0 }, methods: { toggle () { this.isShow = !this.isShow; }, changeWrap (num) { this.tag = num; }, changeMain (num) { // this.flag num this.flag = num; }, whoShow (num) { // this.flag num return this.flag == num; } } }) </script>
循环指令
<div id="app"> <h1>{{ msg }}</h1> <!-- v-for="item in items" --> <!-- 遍历的对象: 数组[] 对象(字典){} --> <ul> <li>{{ list[0] }}</li> <li>{{ list[1] }}</li> <li>{{ list[2] }}</li> <li>{{ list[3] }}</li> <li>{{ list[4] }}</li> </ul> <!-- n为遍历的元素值 --> <ul> <li v-for="n in list">{{ n }}</li> </ul> <!-- 一般列表渲染需要建立缓存 --> <!-- 列表渲染是循环,需要赋值变量给key,使用key需要v-bind:处理 --> <!-- v-for变量数组[]时,接收两个值时,第一个为元素值,第二个为元素索引 --> <ul> <li v-for="(n, i) in list" :key="i">value:{{ n }} | index: {{ i }}</li> </ul> <ul> <li>{{ dic['name'] }}</li> <li>{{ dic.age }}</li> <li>{{ dic.gender }}</li> </ul> <!-- v-for变量对象{}时,接收三个值时,第一个为元素值,第二个为元素键,第三个为元素索引 --> <ul> <li v-for="(v, k, i) in dic" :key="k">value:{{ v }} | key:{{ k }} | index: {{ i }}</li> </ul> <!-- 遍历的嵌套 --> <div v-for="(person, index) in persons" :key="index" style="height: 21px;"> <div v-for="(v, k) in person" :key="k" style="float: left;">{{ k }} : {{ v }} </div> </div> </div> <script type="text/javascript"> new Vue({ el: "#app", data: { msg: "列表渲染", list: [1, 2, 3, 4, 5], dic: { name: 'zero', age: 88888, gender: 'god' }, persons: [ {name: "zero", age: 8}, {name: "egon", age: 78}, {name: "liuXX", age: 77}, {name: "yXX", age: 38} ] } }) </script>
组件
- 每一个组件都是一个vue实例
- 每个组件均具有自身的模板template,根组件的模板就是挂载点
- 每个组件模板只能拥有一个根标签
- 子组件的数据具有作用域,以达到组件的复用
根组件
<div id="app"> <h1>{{ msg }}</h1> </div> <script type="text/javascript"> // 通过new Vue创建的实例就是根组件(实例与组件一一对应,一个实例就是一个组件) // 每个组件组件均拥有模板,template var app = new Vue({ // 根组件的模板就是挂载点 el: "#app", data : { msg: "根组件" }, // 模板: 由""包裹的html代码块,出现在组件的内部,赋值给组件的$template变量 // 显式书写模块,就会替换挂载点,但根组件必须拥有挂载点 template: "<div>显式模板</div>" }) // app.$template </script>
局部组件
<div id="app"> <local-tag></local-tag> <local-tag></local-tag> </div> <script> var localTag = { data () { return { count: 0 } }, template: '<button @click="btnAction">局部{{ count }}</button>', methods: { btnAction () { this.count ++ } } } new Vue({ el: "#app", components: { 'local-tag': localTag } }) </script>
全局组件
<div id="app"> <global-tag></global-tag> <global-tag></global-tag> </div> <script> Vue.component('global-tag', { data () { return { count: 0 } }, template: '<button @click="btnAction">全局{{ count }}</button>', methods: { btnAction () { this.count ++ } } }) new Vue({ el: "#app" }) </script>
父组件传递数据给子组件
- 通过绑定属性的方式进行数据传递
<div id="app"> <global-tag :sup_data1='sup_data1' :supData2='sup_data2'></global-tag> </div> <script type="text/javascript"> Vue.component('global-tag', { props:['sup_data1', 'supdata2'], template: '<div>{{ sup_data1 }} {{ supdata2 }}</div>' }) new Vue({ el: '#app', data: { sup_data1: '数据1', sup_data2: '数据2' } }) </script>
子组件传递数据给父组件
- 通过发送事件请求的方式进行数据传递
<div id="app"> <global-tag @send_action='receiveAction'></global-tag> </div> <script type="text/javascript"> Vue.component('global-tag', { data () { return { sub_data1: "数据1", sub_data2: '数据2' } }, template: '<div @click="clickAction">发生</div>', methods: { clickAction () { this.$emit('send_action', this.sub_data1, this.sub_data2) } } }) new Vue({ el: '#app', methods: { receiveAction (v1, v2) { console.log(v1, v2) } } }) </script>
生命周期钩子
- 表示一个Vue实例从创建到销毁的这个过程,将这个过程的一些时间节点赋予了对应的钩子函数
- 钩子函数: 满足特点条件被回调的方法
new Vue({ el: "#app", data: { msg: "message" }, beforeCreate () { console.log("实例刚刚创建"); console.log(this.msg }, created () { console.log("实例创建成功, data, methods已拥有"); console.log(this.msg); }, mounted () { console.log("页面已被vue实例渲染, data, methods已更新"); } // 拿到需求 => 确定钩子函数 => 解决需求的逻辑代码块 })
Vue-CLI 项目搭建
1、环境搭建
- 安装node
官网下载安装包,傻瓜式安装:https://nodejs.org/zh-cn/
- 安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
- 安装脚手架
cnpm install -g @vue/cli
- 清空缓存处理(如果上面的步骤出现错误可以使用这个方法来清空后重新安装)
npm cache clean --force
2、Vue项目常用命令
- 创建项目
vue create 项目名
- 启动/停止项目
npm run serve / ctrl+c
- 打包项目
npm run build
3、Vue项目
- 项目目录
dist: 打包的项目目录(打包后会生成) node_modules: 项目依赖 public: 共用资源 src: 项目目标,书写代码的地方 -- assets:资源 -- components:组件 -- views:视图组件 -- App.vue:根组件 -- main.js: 入口js -- router.js: 路由文件 -- store.js: 状态库文件 vue.config.js: 项目配置文件(没有可以自己新建)
- main.js
new Vue({ el: "#app", router: router, store: store, render: function (h) { return h(App) } })
- .Vue文件
<template> <!-- 模板区域 --> </template> <script> // 逻辑代码区域 // 该语法和script绑定出现 export default { } </script> <style scoped> /* 样式区域 */ /* scoped表示这里的样式只适用于组件内部, scoped与style绑定出现 */ </style>
项目功能插件
vue-router
{ path: '/', name: 'home', // 路由的重定向 redirect: '/home' } { // 一级路由, 在根组件中被渲染, 替换根组件的<router-view/>标签 path: '/one-view', name: 'one', component: () => import('./views/OneView.vue') } { // 多级路由, 在根组件中被渲染, 替换根组件的<router-view/>标签 path: '/one-view/one-detail', component: () => import('./views/OneDetail.vue'), // 子路由, 在所属路由指向的组件中被渲染, 替换该组件(OneDetail)的<router-view/>标签 children: [{ path: 'show', component: () => import('./components/OneShow.vue') }] } <!-- router-link渲染为a标签 --> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> | <router-link :to="{name: 'one'}">One</router-link> | <!-- 为路由渲染的组件占位 --> <router-view /> a.router-link-exact-active { color: #42b983; } // router的逻辑转跳 this.$router.push('/one-view') // router采用history方式访问上一级 this.$router.go(-1)
vuex
// 在任何一个组件中,均可以通过this.$store.state.msg访问msg的数据 // state永远只能拥有一种状态值 state: { msg: "状态管理器" }, // 让state拥有多个状态值 mutations: { // 在一个一个组件中,均可以通过this.$store.commit('setMsg', new_msg)来修改state中的msg setMsg(state, new_msg) { state.msg = new_msg } }, // 让mutations拥有多个状态值 actions: { }
vue-cookies
// 安装cookie的命令 // npm install vue-cookies --save // 为项目配置全局vue-cookie import VueCookies from 'vue-cookies' // 将插件设置给Vue原型,作为全局的属性,在任何地方都可以通过this.$cookie进行访问 Vue.prototype.$cookies = VueCookies // 持久化存储val的值到cookie中 this.$cookies.set('val', this.val, 300) // 获取cookie中val字段值 this.$cookies.get('val') // 删除cookie键值对 this.$cookies.remove('val')
axios
// 安装 axios(ajax)的命令 // npm install axios--save // 为项目配置全局axios import Axios from 'axios' Vue.prototype.$ajax = Axios let _this = this this.$ajax({ method: 'post', url: 'http://127.0.0.1:5000/loginAction', params: { usr: this.usr, ps: this.ps } }).then(function(res) { // this代表的是回调then这个方法的调用者(axios插件),也就是发生了this的重指向 // 要更新页面的title变量,title属于vue实例 // res为回调的对象,该对象的data属性就是后台返回的数据 _this.title = res.data }).catch(function(err) { window.console.log(err) }) # 用pycharm启动该文件模拟后台 from flask import Flask, request, render_template from flask_cors import CORS app = Flask(__name__) CORS(app, supports_credentials=True) @app.route('/') def index(): return "<h1>主页</h1>" @app.route('/loginAction', methods=['GET', 'POST']) def test_action(): # print(request.args) # print(request.form) # print(request.values) usr = request.args['usr'] ps = request.args['ps'] if usr != 'abc' or ps != '123': return 'login failed' return 'login success' if __name__ == '__main__': app.run()