Vinson

Vinson

Vue2

发表于 2023-09-12
Vinson
阅读量 26

Vue 指令

Vue 模板指令

{{数据}} // 插值
v-text='数据' // 带标签的数据不会解析
v-html='数据' // 会解析 html 标签的数据
 
v-if='表达式' // 判断真假,创建或删除; 可选 v-else-if/v-else
v-show='表达式' // 显示或隐藏
v-bind:属性名='数据' // 动态绑定属性
 :属性名 // 动态绑定简写
:style // 数组动态更新,对象 {样式:值} 改变不会被检测
:class // 数组动态更新,对象 {类名:true} 改变不能被检测
  // 对象解决方法 --> Vue.set(改变的对象数据,'新属性','值')
  // 数组[下标]不能更新解决办法 --> Vue.set(改变的数组,'下标','值')或splice
js
v-on:事件='方法' // 绑定事件
@事件='方法' // 绑定事件简写
v-bind:[attribute] // 动态参数 2.6.0 +
v-on:[eventName]  // 动态参数

v-for='' // 遍历 in/of 关键字, (item,i) in arr; (val,key,i) of obj;n in 9;
v-model='变量' // 表单双向绑定 
  // text   --> 获取文本框的值; 
  // checkbox --> 单选返回 true/false,多选返回数组[每个复选的 value]; 
  // radio  --> 返回单选的 value 值
  // select  --> 单选值为选中的 value,多选为数组
js
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no">
html
vm.toggle === 'yes' // 当选中时 
vm.toggle === 'no' // 当没有选中时
js

自定义指令

<input v-focus='{i}' type="text" v-model='i'>
Vue.directive('focus', function(el,obj){ // el 原生,obj 参数
  console.log('元素被创建/更新都会被调用');
})

// 注册
Vue.directive('my-directive', {
  bind: function () {}, // 创建/绑定
  inserted: function () {}, // 插入
  update: function () {}, // 更新
  componentUpdated: function () {}, // 组件更新后
  unbind: function () {} // 解绑
})
js

Vue 属性更新检测

数组更新检测

push、pop、shift、unshift、splice、sort、reverse

filter()、concat()、slice() 返回新数组替换旧数据

arr[i] = xx ---> Vue.set(数组,下标,新值) vm.$set(数组,下标,新值) vm.数组.splice(下标,1,新值)
arr.length = n  ---> vm.数组.splice(新长度)
js

对象添加属性

// 全局set
Vue.set(改变的对象,添加的键,值)

// 实例set
实例.$set(改变的对象,添加的键,值)

// 多个属性
改变的对象 = Object.assign({},改变的对象,{对象1},{对象2}...)
// 后面的对象合并到第一个参数,并返回第一个参数的引用,再复制给改变的对象
// 第一个参数需要是新对象,如果是要改变的对象引用不会变
js

对象删除属性

Vue.delete(对象,'属性名')
实例.$delete(对象,'属性名')
js

this.$nextTick

数据被修改且渲染之后再调用next回调,并返回promise对象

this.$nextTick这个方法作用是当数据被修改后使用这个方法会回调获取更新后的dom再渲染出来

methods:{
  testClick() {
​    this.content = '改变了的值'this.$nextTick(() => {
​      // dom元素更新后执行,因此这里能正确打印更改之后的值console.log(that.$refs.tar.innerText) // 改变了的值
​    })
  }
}

js

Vue 生命周期

  • beforeCreate:初始化之前
  • created:初始化之后
  • beforeMount:渲染之前
  • mounted:渲染之后
  • beforeUpdate:更新数据渲染页面之前
  • updated:更新数据渲染页面之后
  • beforeDestroy:销毁之前
  • destroyed:销毁之后
  • activated:被 keep-alive 缓存的组件激活时调用。服务器端渲染期间不被调用
  • errorCaptured:发生错误时触发

Vue 事件

内联语句处理器

@事件=‘方法($event)’ // $event 将当前事件原生 event 传入

事件修饰符

  • .self: 只触发自己的方法,子节点冒泡不触发
  • .stop: 阻止冒泡
  • .prevent: 阻止默认行为
  • .passive: 不阻止默认行为,不能与prevent一起使用
  • .once: 只执行一次
  • .capture: 使用事件捕捉模式
    • @click.prevent.self 会阻止所有的点击
    • @click.self.prevent 只会阻止对元素自身的点击
  • .native: 原生事件

按键修饰符

  • .ascii码值:按下对应的键值触发
  • .enter:回车
  • .tab:table
  • .delete:“删除”和“退格”键
  • .esc:退出
  • .space:空格
  • .up:上
  • .down:下
  • .left:左
  • .right:右

自定义按键修饰符别名

Vue.config.keyCodes.f1 = 112
js

使用 —> @keyup.f1

  • .ctrl
  • .alt
  • .shift
  • .meta:windows/mac 的 command 键

多个按键

@keyup.alt.67    // ctrl+c
@keyup.click.ctrl  // Ctrl + 点击
// 如果同时加了其他的按键按下也会触发
js
  • .exact精确的修饰符按下事件
click.ctrl.exact // 只按下 ctrl 才能触发,多余按下不能触发
click.exact    // 不按下任何键才能触发
js

鼠标修饰符

  • .left
  • .right
  • .middle

表单修饰符

  • .lazy:value 改变且失焦才触发,在 change 事件中同步
  • .number:将输入的值转为 number 类型
  • .trim:过滤输入的首位空格

Vue computed 计算属性与 watch 监听

computed 计算属性

依赖 data 的数据改变,computed 就会刷新

{{调用不需要加()}}
data : {
  a : 1 // a 改变,fn 会刷新
},
computed: {
  fn(){
    return this.a + 10;
  }
}
js

getter,setter

计算属性默认只有 getter

computed: {
  name: {
​    // getterget: function () {},
​    // setterset: function (newValue) {}
  }
}
// name = 'a' getter会被调用
js

watch 侦听属性

data:{name:'name',obj:{...}},
watch:{
  name (新值,旧值){},// 浅监听
  // 深监听
  obj:{
    deep:true, // deep 打开深监听
    immediate:true, // immediate 立即执行
    handler(新值){} // handler 只有新值
  }
}
js

name 发生改变时 watch 发生回调

Vue filter 过滤器

  1. 通过 | 竖线管道符 把前面的数据传入|右边的方法
  2. 局部注册 filters :{方法名(val){}}
  3. 全局注册 Vue.filter(‘方法名’,(val) = > {})
  4. golbal(‘参数’)传入的参数, 方法内golbal(val,参数){} 接收形参第一个参数为 | 前面的值,第二个开始才是传进来的参数
{{1+2 | fn1 | golbal('sss')}}
Vue.filter('golbal',(val,a) => val + a)// 全局注册
let vm = new Vue({
  el:'#box',
  filters:{
​    fn1 : (val) => val.toFixed(2) // 局部注册,可以多个
  }
})
js

Vue transition 动画过渡

class类

  • 显示 name 为自定义组件上 name

    • name-enter:进入前
    • name-enter-active:进入中
    • name-enter:进入后
  • 消失

    • name-leave:消失前
    • name-leave-active:消失中
    • name-leave-to:消失后
<style>
.name-enter,.name-leave-to{
  width: 0;
}
.name-enter-active,.name-leave-active{
  transition: all .3s;
}
.name-enter-to,.name-leave{
  width: 100px;
}
</style>

</head>
<body>
<div id="box">
  <button @click='tog'>click</button>
  <transition name='name'>
        <div v-show='fg'></div>
  </transition>
</div>
html

组件属性 attr-class

  • enter-class:进入前
  • enter-active-class:进入中
  • enter-to-class:进入后
  • leave-class:离开前
  • leave-active-class:离开中
  • leave-to-class:离开后

4.1.1 以上 animate.css,需要加 animate__animated 与 animate__ 动画名

<transition
  enter-active-class="animate__animated animate__bounce"
  leave-active-class="animate__animated animate__bounceOutRight">
\> 
    <div v-show='fg' class="animated"></div>
</transition>
html

多个元素过渡

<transition enter-active-class="animate__animated animate__fadeInRight" 
leave-active-class="animate__animated animate__fadeOutUpBig">
    <div v-if='isshow' key="1"></div>
    <div v-else key="2"></div>
</transition>
html

列表过渡

tag 把外层包装为 ul 标签,默认是 span

<transition-group tag="ul" 
  enter-active-class="animate__animated animate__fadeInRight" 
  leave-active-class="animate__animated animate__fadeOutUpBig"
\> 
  <li v-for="(item,index) in items" :key="item.id" @click='del(index)'>
​    {{ item }}
  </li>
</transition-group>
html

过渡模式

  • 在 transtion 上添加属性 mode
  • in-out: 新元素先进行过渡,完成之后当前元素过渡离开。(一般不用)
  • out-in: 当前元素先进行过渡,完成之后新元素过渡进入。

Vue 组件通讯与插槽事件分发

// 全局组件 [注意] 必须在new Vue之前
Vue.component('组件名',{
  template:`html模板`,
  // data必须为函数
  data() {
​    return {}
  },
  // 局部组件
  components : {
​    组件名:{}
  }
});
// 根组件
new Vue({el:'#app'});
js

父传子

  1. 接收的变量与父组件的变量名一致
  2. 对象写法验证支持的类型,Number,String,Boolean,Array,Object,Function,null(不限制数据类型)
props:[接收的变量]
props:{
  接收的变量:限制接收的数据类型,
  接收的变量:{
​    type : [Number, String], // 单个或多个default : '默认值',
​    required : true, // 必填项
}

// 自定义校验器
  接收的变量: {
​    validator(val){
​      return xxx
​    }
  }
}
js
<nav1 :val='a'></nav1>

Vue.component('nav1',{
  template:`<h1>{{val}}</h1>`,
  props:['val'] // 
})

let vm = new Vue({
  el:'#app',
  data : {
​    a : 1
  },
})
js

子传父

  1. <子组件 @自定义事件=‘父组件事件’></子组件>
  2. 子组件 this.$emit(‘自定义事件’,传参,[可选多个参数])
<div id="box">
  <!-- nn被子组件调用,ff为父组件方法 -->
  <qj @nn='ff' :val='a'></qj>
</div>

<script>
Vue.component('qj',{
  template : `<button @click=ff>click</button>`,
  data() {
​    return { child : '子组件值' }
  },
  methods: {
​    ff(){ // 调用自定义事件this.$emit('nn',this.child)
​    }
  },
})
let vm = new Vue({
  el:'#box',
  methods: {
​    ff(n){
​      console.log(n);
​    }
  }
})
</script>
html

组件通信

  1. let bus = new Vue(); new 一个空 vue 作为中间传递
  2. bus.$emit(‘自定义事件’,‘发送的值’) 发送数据
  3. bus.$on(‘自定义事件’,回调函数) 接收数据
  4. [注意] 脚手架需要在 main.js 中 Vue实例之前在 Vue.prototype 属性添加 new Vue()
let bus = new Vue();
Vue.component('c1',{
  template:`<div><button @click='fn1()'>click</button></div>`,
  methods: {
​    fn1(){
​      bus.$emit('msg','值')
​    }
  },
})

Vue.component('a2',{
  template : `<div></div>`,
  mounted() {
​    bus.$on('msg',d => { console.log(d); })
  },
})
js

单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定,父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop 将其作为一个本地的 prop 数据来使用

props: ['initialCounter'],
data: function () {
 return {
  counter: this.initialCounter
 }
}
js

ref 通信

  1. <组件 ref=‘自定义组件名字’></组件>
  2. 父组件操控:this.$refs.自定义组件名字 拿到实例
<slide ref='child'></slide>
Vue.component('slide',{
  template : '<div></div>',
  methods: {fn(){}}
})

let vm = new Vue({
  el:'#box',
  methods: {
​    getChild(){
​      console.log(this.$refs.child); // 拿到子组件的实例对象
​      this.$refs.child.fn()
​    }
  }
})

组件使用 v-model

  1. props 接收父级数据,组件使用 $emit(‘input’,$event.target.value)发送
  2. [原理] v-model 等价 :value=p 和 @input ;$emit 调用 @input,发送当前 value
<child v-model='p'></child>

Vue.component('child',{
  template : `<input :value='p' @input='$emit("input",$event.target.value)'>`,
  props : ['p']
})
html

动态组件

  1. 使用 component 标签,绑定 is 属性,对应的值为组件名称
  2. 外层添加 keep-alive 可以在切换之后避免重新渲染元素
<keep-alive> <!-- keep-alive 可以避免动态组件重新渲染,每次切换不会删除之前的 -->
  <!-- 改变 is 属性,is 的属性为组件的名字。每次切换组件后会重新渲染 -->
  <component :is='who'></component> component会被替换为who组件的标签
</keep-alive>
html

slot插槽

  1. 插槽显示顺序以子组件的为准
  2. 匿名插槽对应匿名
  3. 具名插槽父组件属性 slot=‘名字’ 子组件 对应
  4. 多个标签使用 template 标签并加上 slot='名字’属性。简写 #名字
  5. 作用域插槽,子组件动态绑定属性 :键=‘值’,name='名字’对应 父组件 #名字=‘接收参数’。接收的参数为对象,对象中包含子组件传入的键和值

父组件

<ss>
  <mark>匿名插槽</mark>
    <p slot="top">具名插槽</p>
  <!-- 老语法 slot='' 新语法 #xxx 只能在template使用 -->
  <template #bom> 
    <div>多个标签</div>
    <p>多个标签</p>
  </template>
  <template #list='props'>
    <div>{{props.objkey}}</div>
  </template>
</ss>
html

子组件

<div>
  <slot name="default"></slot> 
  <!-- 匿名插槽默认default -->
  <slot name='top'></slot>
<slot name='bom'></slot>
<!-- 遍历数组,传出对象 {objkey:item,...}  -->
  <slot v-for="item of arr" :objkey='item' name="list"></slot>
</div>
html

VueCli 脚手架

安装创建

  • cnpm i @vue/cli -g 安装新版
  • cnpm I @vue/cli-init -g 安装旧版
  • npm r vue-cli 卸载旧版
  • where vue 查看老版本位置
  • vue -V 查看安装版本
  • vue init webpack 项目名 老版本创建项目
  • vue create 项目名 — 选择vue 2或3的语法
Project description A Vue.js project 项目描述
Author tomiaa <1178852449@qq.com>  邮箱
Vue build (Use arrow keys) 
Vue build standalone  独立生产环境
Install vue-router? Yes 安装路由
Use ESLint to lint your code? No 代码规范
Set up unit tests No  是否单元测试
Setup e2e tests with Nightwatch? No  测试
Should we run `npm install` for you after the project has been created? (recommended) npm 下载方式
sh

全局使用包

需要在 main.js 在 Vue 的原型上添加

import Vue from 'vue'
import $ from 'jquery';
Vue.prototype.bus = new Vue();
Vue.prototype.$ = $;
js

Vue router 路由

npm install vue-router -S
sh
router/index.js
// 引入依赖
import Vue from 'vue'
import Router from 'vue-router'
// 导出实例
export default new Router({
 routes: [
  {
   path: '/',
   name: 'HelloWorld/',
   component: HelloWorld
  }
 ]}
)
js

main.js

import App from './App'
import router from './router' // 引入 router
new Vue({
 el: '#app',
 router, // 添加
 components: { App },
 template: '<App/>'
})
js

App.js

添加 router 视口

<router-view />
html

路由原理

路由原理:

  1. hash路由 ==> location.hash 切换
    window.onhashchange 监听路径的切换
  2. history路由==> history.pushState 切换
    window.onpopstate 监听路径的切换

路由表

Vue.use(Router)
new Router({
 mode : 'history', // history模式
 base : 'vue', // 路由前缀  访问时地址自动添加base
 routes: [
  {
   path: '/',
   name: 'HelloWorld', // 路由命名
   components:{ // 视图命名default : HelloWorld,
​     a : topbar,
​     b : shopcar
   },
   meta : { // 元信息
​    title :'HelloWorld'
   },
   alias : '/biemin', // 别名 访问/biemin会跳转到 path路径
   // 嵌套路由children: [{
​    path: 'bar',
​    component: Bar,
​    meta: { requiresAuth: true }
​    }]
  }]
})
js

视图命名

<router-view /> 没有name默认default
<router-view name="a"></router-view>
<router-view name="b"></router-view>
components:{ // 视图命名
  default : HelloWorld,
  a : topbar,
  b : shopcar
}
html

重定向

// 第一种处理404
{
 path : '',
 component : notFound
},

// 第二种404处理 重定向
{
 path : '',
 redirect : '/notFound'
},

// 上面没有匹配到 进入 再进入notFound
{
 path:'/notFound',
 component : notFound
}
js

声明式导航

  1. 默认渲染为a标签,tag设置渲染标签类型
  2. to属性路由地址,可以是字符串,变量,对象{path:‘地址’},{name:‘命名路由’}(需要与router中name一致)
  3. 匹配的标签calss默认增加 router-link-exact-active完全匹配 与 router-link-active 局部匹配 [ / 也会匹配 ]
  4. to=‘/’ 会在其他路由中匹配,所以也会加上 router-link-active
  5. active-class=“类名” 属性 自定义类名
  6. replace 替换历史记录,点击后没有历史记录
<router-link to="/" tag="span" replace>Go to home</router-link>
<router-link to="/topbar" active-class="activeBar">Go to topbar</router-link>
<router-link to="/shop" active-class="activeBar">Go to shopcar</router-link>
<router-link :to="{path : '/shop'}" >Go to shopcar</router-link>

<!-- 路由匹配到的组件将渲染在这里 -->
<router-view />
html

query 传参会显示在地址栏,刷新不会丢失。

params 传参需要用 name,且刷新会丢失。params 传参也可以使用动态路由显示在地址栏,如在 Router 表中定义 path: ‘/home/:id’

命名路由:

<router-link :to="{name : 's'}" >Go to shopcar</router-link>
new Router({
  routes: [{
    path: '/shop',
    name: 's',
    component: s
  }]
})
js

编程式导航

replace 方法与 push 一样,但不会留下历史记录

router.push('home')// 字符串
router.push({ path: 'home' })// 对象
router.push({ name: 'user', params: { userId: '123' }})// 命名的路由
router.push({ path: 'register', query: { plan: 'private' }})// 带查询参数,变成 /register?plan=private
router.go(n) // 历史记录不够用则失败
router.go(1)// 在浏览器记录中前进一步,等同于 history.forward()
router.go(-1)// 后退一步记录,等同于 history.back()
router.go(3)// 前进 3 步记录
router.go(n)// 执行浏览器的前进动作,n代表前进的步数,负数代表后退
router.back()// 执行浏览器的后退动作
router.forward()
js

重复导航当前路径报错问题

在 Router 暴露之前重写 push 方法

const routerPush = Router.prototype.push;
Router.prototype.push = function push(location) {
  return routerPush.call(this, location).catch(error=> error)
}
js

动态路由

  1. /detail/:变量名 设计路由
  2. 新页面 $route.params 接参 只有动态路由才有 params
  3. $route.query 是 ? 后面的参数,每个页面都可以有 不需要改动路由表
<router-link to="/detail/cansu "></router-link>
html

路由设计

{
  path : '/detail/:name',
  component : detail
}
js

解决首屏加载慢的问题

import shopcar from '@/components/shopcar'
import notFound from '@/components/notFound' 
// 改为
const HelloWorld = () => import('@/components/HelloWorld')
const topbar = () => import('@/components/topbar')
js

路由守卫

  • to – 要跳转到新的路由 router 对象
  • from – 跳转之前的路由 router 对象
  • next – 执行后续的中间件
  • next(false) 中间当前的导航,并跳转到 from 对应的地址
  • next(‘/地址’) / next({path:‘/地址’}) 跳转到指定的路径

全局守卫 – 写在 router 全局

router.beforeEach((to,from,next) => {}) // 进入之前
router.afterEach((to,from) => {}) // 离开之前
js

路由守卫 – 写在路由表

beforeEnter: (to, from, next) => {} 
js

组件守卫 – 写在组件内

beforeRouteEnter(to, from, next) {
 // 该组件对应的路由被确认之前调用
 // 不能使用 this,实例还未被创建
}
beforeRouteUpdata(to, from, next) {
 // 路由被改变,组件被复用时,可以访问 this
}
beforeRouteLeave (to, from, next) {
 // 离开该组件对应的路由 可以调用 this
}
js

嵌套路由

{
 path: '/topbar',
 component: topbar,
 meta : {title :'topbar'},
 redirect : '/topbar/child1', // 打开 topbar 即重定向到 child1
 children : [ // path 开头不带 / 为从上一层 /topbar 链接的地址  ---> /topbar/child1。带/需要路径补充完整
  {path : 'child1',component:child1},
  {path : 'child2',component:child2},
  {path : 'child3',component:child3},
 ]
}
js

Vuex 状态管理

  • state:储存状态
  • getters:获取状态
    • 两个参数: state 状态,getter 本身 调用方法传入参数可以使用闭包
  • mutation:修改状态 – 方法名应单独使用文件定义为产量
    • 两个参数: state 状态, commit 提交的参数
    • 通过commit提交修改 commit(‘修改状态的方法’,参数) 必须为同步操作,异步需要在 action 中进行后 context.commit 提交
    • commit:参数为对象时提交 payload 为 commit 的对象
    • store.commit({ type: ‘increment’, amount: 10 })​ - increment (state, payload) { state.count += payload.amount }
  • action:异步操作
    • $store.dispatch(‘调用action方法’,参数)
    • 一个参数:context 指向 $store —> 使用 context.commit 提交修改
    • 使用 {commit} 接参就是 context 内的提交方法
  • modules:模块
    • 模块定义 state 状态 获取属性 $store.state.模块名.属性
    • getters 三个参数: state 状态,getter 本身,rootState 根的状态
    • action 内 context 对应的是当前模块的上下文,包含 context.rootGetter为根的方法,rootState 根的状态

辅助函数

  1. mapState, mapGetters 写在 computed 里面, mapMutations,mapActions 则是在 methods 方法内
  2. [函数名/属性名] 映射相同名字或方法 修改名字需要{}形式 ,key为键,值为对应的方法
  3. 调用辅助函数返回对象,…展开后可以添加其他方法
  4. 返回的数据只是作为映射,方法/计算属性需要传参需要调用(传参)

引入

import {mapState,mapGetters,mapMutations,mapActions} from 'vuex';
computed : {
  ...mapGetters(['ride']),
  ...mapState({
​    arrNum : 'num',
  })
},
methods: {
  ...mapMutations({ square:SQUARE}),// 修改名字为square映射
  ...mapActions( {addnum:ADDNUM}),
}
js

传参

{{ride(3)}}

<button @click="addnum(3)">mapAction</button>
html

命名空间

模块 namespaced 开启

modules: {
  aa : {
​    namespaced : true // 开启命名空间
  }
}
js

第一种

import {createNamespacedHelpers} from 'vuex'; // 引入
const {mapState} = createNamespacedHelpers('命名空间名') // 创建
computed : {
  ...mapState['状态名']
}
js

第二种

import {mapState} from 'vuex';
computed : {
  ...mapState['命名空间名',['状态名']]
}
js

Vue 跨域代理

代理配置

2.0

proxyTable: {
 '/api': {
  target: 'http:// 127.0.0.1:3000/api', // 接口的域名
  // secure: false, // 如果是https接口,需要配置这个参数
  changeOrigin: true, // 如果接口跨域,需要进行这个参数配置,为true的话,请求的header将会设置为匹配目标服务器的规则(Access-Control-Allow-Origin)
  pathRewrite: {
    '^/api': '' // 本身的接口地址没有 '/api' 这种通用前缀,所以要rewrite,如果本身有则去掉 
  }
 }
}
js

3.0

// vue.config.js  脚手架的配置文件
module.exports = {
  // 选项...
  // 配置跨域
  devServer: {
​    proxy: {
​      '/api': {
​        target: 'http:// localhost:3000// api', // 接口的域名// secure: false, // 如果是 https 接口,需要配置这个参数changeOrigin: true, // 如果接口跨域,需要进行这个参数配置,为 true 的话,请求的 header 将会设置为匹配目标服务器的规则(Access-Control-Allow-Origin)pathRewrite: {
​          '^/api': '' // 本身的接口地址没有 '/api' 这种通用前缀,所以要rewrite,如果本身有则去掉 
​        }
​      }
​    }
  }
}
js

配置 baseUrl

// 与axios同级  axios 引入 {api_url}
// ================== 区分环境,配置接口地址
let host, api_url;
if (process.env.NODE_ENV == "development") { // 开发环境
  host = "http:// localhost:3000",
​ // api_url = "http:// localhost:3000/api"
​ api_url = "/api"
}
if (process.env.NODE_ENV == "production") { // 生产环境
  host = "http:// localhost:3000",
​  api_url = "http:// localhost:3000/api"
}  
export let HOST = host;
export let API_URL = api_url;
js

Vue Node.js 服务端渲染

依赖

"dependencies": {
  "express": "^4.17.1",
  "vue": "^2.6.12",
  "vue-server-renderer": "^2.6.12"
}
json

html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
 {{{meta}}}
 <title>{{title}}</title>
</head>
<body>
 <!--vue-ssr-outlet-->
</body>
</html>
html

app.js

const Vue = require('vue');
const server = require('express')()
const fs = require('fs')

const renderer = require('vue-server-renderer').createRenderer({
 // 读文件,返回给浏览器去显示
 template: fs.readFileSync('./index.html', 'utf-8')
})

// 页面的 head 部分
const context = {
 title: 'vue-ssr-demo-title',
 meta: 
  `<meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  `
}

server.get('/huaweip40', (req, res) => {
 const vm = new Vue({
  data: {
   url: req.url,
   msg: 'huaweip40',
   hobby: ['唱','跳','rap','打篮球']
  },
  template:
   `<div>
        <p>当前页面地址:{{url}}</p>
        <p>{{msg}}</p>
   </div>
   `
 })
 renderer.renderToString(vm, context, (err, html) => {
  // 失败的回调
  if(err) {
   res.status(500).end('err', err)
   return
  }
  // 服务器给浏览器发送的数据
  res.end(html)
 })
})

server.get('/mi11', (req, res) => {
 const vm = new Vue({
  data: {
   url: req.url,
   msg: 'mi11',
   hobby: ['唱','跳','rap','打篮球']
  },
  template:
   `<div>
        <p>当前页面地址:{{url}}</p>
        <p>{{msg}}</p>
   </div>
   `
 })
 
 renderer.renderToString(vm, context, (err, html) => {
  // 失败的回调
  if(err) {
   res.status(500).end('err', err)
   return
  }
  // 服务器给浏览器发送的数据
  res.end(html)
 })
})
const port = 8082
server.listen(port, ()=> {
 console.log('ok!!!!!!', port);
})
js

nuxt 基础使用

安装

npx create-nuxt-app <项目名>
js

目录结构

└─my-nuxt-demo
├─.nuxt // Nuxt自动生成,临时的用于编辑的文件,build
├─assets // 用于组织未编译的静态资源如LESS、SASS或JavaScript,对于不需要通过 Webpack 处理的静态资源文件,可以放置在 static 目录中
├─components // 用于自己编写的Vue组件,比如日历组件、分页组件
├─layouts // 布局目录,用于组织应用的布局组件,不可更改⭐
├─middleware // 用于存放中间件
├─node_modules
├─pages // 用于组织应用的路由及视图,Nuxt.js根据该目录结构自动生成对应的路由配置,文件名不可更改⭐
├─plugins // 用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
├─static // 用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。文件夹名不可更改。⭐
└─store // 用于组织应用的Vuex 状态管理。文件夹名不可更改。⭐
├─.editorconfig // 开发工具格式配置
├─.eslintrc.js // ESLint的配置文件,用于检查代码格式
├─.gitignore // 配置git忽略文件
├─nuxt.config.js // 用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。文件名不可更改。⭐
├─package-lock.json // npm自动生成,用于帮助package的统一设置的,yarn也有相同的操作
├─package.json // npm 包管理配置文件
├─README.md

shell

页面及路由

└─pages
├─index.vue
└─user
├─index.vue
├─one.vue

shell

nuxt会自动生成路由, 访问/是pages下的index.vue 访问/user是user下的index.vue 访问/user/one 也是

页面跳转

不要写a链接,因为是重新获取一个新的页面,而不是SPA

<nuxt-link to="/users"></nuxt-link> 声明导航
this.$router.push('/users') 编程导航
html

动态路由

需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。

验证动态路由参数, 返回 true 表示通过,false 会跳转到 404 页面

export default {
  validate(data) {
​    cosole.log(data)
​    return true
  }
}
js

异步请求

export default {
  async asyncData({ params, $http }) {
   const post = await $http.$get(`https://api.nuxtjs.dev/posts/${params.id}`)
   return { post }
  }
}
js

asyncData 在服务端发送请求不存在跨域

因为 created 会触发两次,mounted 只会在浏览器触发一次

自定义路由

nuxt.config.js

router: {
  extendRoutes(routes) {
​    routes.push({
​      path: '/',
​      redirect: '/center'
​    })
  }
}
js
评论
来发一针见血的评论吧!
表情

快来发表评论吧~

推荐文章
  • 测试文章

    20点赞10评论

  • webpack5(一)

    20点赞10评论