前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue+Vue-Router+Vuex+SSR项目

Vue+Vue-Router+Vuex+SSR项目

作者头像
达达前端
发布2022-04-13 20:24:56
7690
发布2022-04-13 20:24:56
举报
文章被收录于专栏:达达前端达达前端

Vue-Vue-Router-Vuex-SSR

  1. Vue+Webpack工程流搭建
  2. Vue+Vue-Router+Vuex项目架构

服务端渲染

现在的前端框架是纯客户端渲染的,(请求🤴网站的时候,返回的html是没有什么内容的),存在问题是没有办法seo, 白屏时间较长。需要等待js加载完成,执行完成之后才会显示内容。

服务端渲染解决这些问题。

webpack升级注意 ⚠️ :1. 版本变化 2. 配置变化 3. 插件变化

vue-loader配置

代码语言:javascript
复制
const isDev = process.env.NODE_ENV === 'development'
代码语言:javascript
复制
// vue-loader.config.js
const docsLoader = require.resolve('./doc-loader')
module.exports = (isDev) => {
 return {
  preserveWhitespace: true, // 去掉后面的空格
  extractCSS: !isDev, // 单独打包到css某文件
  cssModules: {},
  // hotReload: false, // 根据环境变量生成
  loaders: {
   'docs': docsLoader,
  },
  preLoader: {
  },
 }
}
代码语言:javascript
复制
module.exports = (isDev) => {
 return {
  preserveWhitespace: true,
  extractCSS: !isDev,
  cssModules: {},
 }
}

css module配置

代码语言:javascript
复制
module.exports = (isDev) => {
 return {
  preserveWhitespace: true,
  extractCSS: !isDev,
  cssModules: {
   localIdentName: isDev ? '[path]-[name]-[hash:base64:5]' : '[hash:base64:5]',
   // localIdentName: '[path]-[name]-[hash:base64:5]',
   camelCase: true
  }
 }
}

安装使用eslint和editorconfig以及precommit

代码语言:javascript
复制
npm i eslint eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node

创建.eslintrc

代码语言:javascript
复制
{
 "extends": "standard"
}
代码语言:javascript
复制
npm i eslint-plugin-html
代码语言:javascript
复制
// package.json

"script": {
 "clean": "rimraf dist",
 "lint": "eslint --ext .js --ext .jsx --ext .vue client/",
 "build": "npm run clean && npm run build:client",
 //自动修复
 "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue client/"
 "precommit": "npm run lint-fix",
}
代码语言:javascript
复制
npm i webpack -D
// webpack 4
npm uninstall webpack webpack-dev-server webpack-merge -D
// webpack 升级
npm i webpack webpack-dev-server webpack-merge webpack-cli -D

npm uninstall babel-loader extract-text-webpack-plugin file-loader html-webpack-plugin -D

webpack.config.base.js

代码语言:javascript
复制
const config = {
 mode: process.env.NODE_ENV, // development || production
 target: 'web',
}
代码语言:javascript
复制
npm i eslint-loader babel-eslint

Vue的一些点

代码语言:javascript
复制
import Vue from 'vue'

const div = document.createElement('div')
document.body.appendChild(div)

new Vue({
 el: div,
 template: '<div>this is content</div>'
})

// webpack.config.practice.js
resolve: {
 alias: {
  'vue': path.join(__dirname, '../node_modules/vue/dist/vue.esm.js')
 }
}

index.js

代码语言:javascript
复制
import Vue from 'vue'
import App from './app.vue'

import './assets/styles/global.style'

const root = document.createElement('div')
document.body.appendChild(root)

new Vue({
 rendeer: (h) => h(App)
}).$mount(root)

VUE实例

  • Vue实例的创建和作用
  • Vue实例的属性
  • Vue实例的方法
代码语言:javascript
复制
import Vue from 'vue'
const app = new Vue({
 // el: '#root',
 template: '<div ref="div">{{text}}</div>',
 data: {
  text: 0
 }
})

app.$mount('#root')

setInterval(()=>{
 // app.text += 1
 // app.$options.data += 1 不可以使用
 app.$data.text += 1 // 可以使用
},1000)

console.log(app.$data)
console.log(app.$props)
console.log(app.$el)
console.log(app.$options)

console.log(app.$slots)
console.log(app.$scopedSlots)

console.log(app.$ref) // div节点/组件实例

console.log(app.$isServer) // 服务端渲染

app.$options.render = (h) => {
 return h('div', {}, 'new render function')
}
代码语言:javascript
复制
props: {
 filter: {
  type: String,
  required: true
 },
 todos: {
  type: Array,
  required: true
 }
}

watch

代码语言:javascript
复制
const app = new Vue({
 // el: '#root',
 template: '<div ref="div">{{text}}</div>',
 data: {
  text: 0
 },
 watch: {
  text(newText, oldText) {
   console.log('${newText}:${oldText}')
  }
 }
})
app.$mount('#root')
代码语言:javascript
复制
const unWatch = app.$watch('text', (newText, oldText){
 console.log(`${nextText}:${oldText}`)
})
setTimeout(()=>{
 unWatch() // 取消 2秒后
}, 2000)

事件监听:

代码语言:javascript
复制
app.$on('test', () => {
 console.log('test emited')
})

//触发事件
app.$emit('test')

app.$on('test', (a, b) => {
 console.log('test emited ${a} ${b}')
})

//触发事件
app.$emit('test', 1, 2)
代码语言:javascript
复制
$once只监听一次
app.$once('test', (a, b) => {
 console.log('test emited ${a} ${b}')
})

forceUpdate强制组件渲染一次

非响应式的:

代码语言:javascript
复制
// 值在变,但不会导致重新渲染
data: {
 text: 0,
 obj: {}
}

setInterval(() => {
 app.obj.a = app.text
 app.$forceUpdate()
},1000)

一直在渲染会让你的性能降低

某个对象上的,某个属性名,给他定义一个值:

代码语言:javascript
复制
let i = 0
setInterval(()=>{
 i++
 app.$set(app.obj, 'a', i) // 变化,某个对象上的某个属性值给它一个值
},1000)

vm.$nextTick([callback])

等DOM节点渲染完成。Vue在下一次更新的时候调用callback

将回调延迟到下次DOM更新循环之后执行,在修改数据之后立即使用它,然后等待DOM更新,它跟全局方法Vue.nextTick一样,不同的是回调的this自动绑定到调用它的实例上。

2.1.0新增,如果没有提供回调且支持Promise的环境中,则返回一个Promise.注意polyfill.

vue生命周期

代码语言:javascript
复制
new Vue({
 el: '#root',
 template: '<div>{{text}}</div>',
 data: {
  text: 'jeskson'
 },
 beforeCreate() {
  console.log(this, 'beforeCreate')
 },
 created() {
  console.log(this, 'created')
 },
 beforeMount() {
  console.log(this, 'beforeMount')
 },
 mounted() {
  console.log(this, 'beforeCreate')
 },
 beforeUpdate() {
  console.log(this, 'beforeUpdate')
 },
 updated() {
  console.log(this, 'updated')
 },
 activated() {
  console.log(this, 'activated')
 },
 deactivated() {
  console.log(this, 'deactivated')
 },
 beforeDestroy() {
  console.log(this, 'beforeDestroy')
 },
 destroyed() {
  console.log(this, 'destroyed')
 },
 render(h) {
  console.log('render function invoked')
  return h('div', {}, this.text)
 },
 renderError(h, err) {
  return h('div', {}, err.stack)
 },
 errorCaptured() {
  // 向上冒泡,并且正式环境可以使用
 }
})
代码语言:javascript
复制
undefined 'beforeCreate'
undefined 'created'
<div id="root"></div> "beforeMount"
<div>0</div> "mounted"

template: `<div> name: {{name}} </div>`

computed: {
 name() {
  return `${this.firstName} ${this.lastName}`
 } 
}

<li v-for="(item, index) in arr">遍历数组</li>
<li v-for="(val, key) in obj"></li>

v-model.number 数字
v-model.trim 去掉空格
<div>
 <input type="radio" value="one" v-model="pick"/>
 <input type="radio" value="two" v-model="pick"/>
</div>

定义组件

代码语言:javascript
复制
import Vue from 'vue'

const compoent = {
 template: '<div>This is compoent</div>'
}

// Vue.component('CompOne', compoent)

new Vue({
 components: {
  CompOne: compoent
 },
 el: '#root',
 template: '<comp-one></comp-one>'
})

// 报错
const compoent = {
 template: '<div>{{text}}</div>',
 data: {
  text: 123
 }
}

[vue warn] the "data" option should be a function that returns a per-instance value in component definitions.

props

代码语言:javascript
复制
// 子组件
const compoent = {
 props: {
  active: Boolean,
  propOne: String
 },
 template: `
  <div>
   <input type="text" v-model="text">
   <span>{{propOne}}</span>
   <span v-show="active">see me if active</span>
  </div>
 `,
 data() {
  return {
   text: 0
  }
 }
}

// 父组件传递
<comp-one :active="true" prop-one="text1"></comp-one>

不允许在组件内部修改this.propOne='inner content',

代码语言:javascript
复制
vue warn: avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "propOne"
代码语言:javascript
复制
// 子组件触发props修改

// 子组件
methods: {
 handleChange() {
  this.$emit('change')
 }
}

// 子组件props

props: ['active', 'propOne'],

props: {
 active: {
  type: Boolean,
  required: true,
  default: true
 }
}

props: {
 active: {
  // type: Boolean,
  // required: true,
  validator(value) {
   return typeof value === 'boolean'
  }
 }
}
代码语言:javascript
复制
const CompVue = Vue.extend(compoent)

new CompVue({
 el: '#root',
 propsData: {
  propOne: 'xxx'
 }
})

🌰

代码语言:javascript
复制
const parent = new Vue({
 name: 'parent'
})

const componet2 = {
 extends: compoent,
 data () {
  return {
   text: 1
  }
 },
 mounted () {
  console.log(this.$parent.$options.name) // Root
  this.$parent.text = '12345'
 }
}

new Vue({
 parent: parent,
 name: 'Root',
 el: '#root',
 mounted () {
  console.log(this.$parent.$options.name) // parent
 },
 components: {
  Comp: component2
 },
 data: {
  text: 2333
 },
 template: `
  <div>{{text}}</div>
  <comp></comp>
 `
})

🌰栗子 input

代码语言:javascript
复制
// 子组件
handleInput (e) {
 this.$emit('input', e.target.value)
}

// 父组件
... :value="value" @input="value = arguments[0]"

// 🌰
const component = {
 props: ['value'],
 template: `
  <div>
   <input type="text" @input="handleInput"></input>
  </div>
 `,
 methods: {
  handleInput (e) {
   this.$emit('input', e.target.value)
  }
 }
}

const component = {
 model:; {
  prop: 'value1',
  event: 'change'
 },
 props: ['value1'],
 template: `
  <div>
   <input type="text" @input="handleInput"></input>
  </div>
 `,
 methods: {
  handleInput (e) {
   this.$emit('change', e.target.value)
  }
 }
}

属性✍️

slot

代码语言:javascript
复制
const component = {
 template: `
  <div :style="style">
   <div class="header">
    <slot name="header></slot>
   </div>
   <div class="body">
    <slot name="body"></slot>
   </div>
  </div>
 `,
 data () {
  return {
   style: {
    width: '200px';
    height: '200px';
    border: '1px solid #aaa'
   }
  }
 }
}

<comp-one>
 <span slot="header"> this is header </span>
 <span slot="body"> this is body </span>
</comp-one>

slot-scope

特殊🌰

代码语言:javascript
复制
template: `
 <div :style="style">
  <slot value="456" aaa="111"></slot>
 </div>
`,

// 父组件 - 组件内部使用的变量
<comp-one>
 <span slot-scope="props"> {{props.value}} {{props.aaa}} </span>
</comp-one>

provide inject

代码语言:javascript
复制
provide : {
 yeye: this,
 value: this.value, // 错误
},

provide() {
 const data = {}

 Object.defineProperty(data, 'value', {
  get: () => this.value,
  enumerable: true
 })

 return {
  yeye: this,
  data
  // value: this.value
 }
}

// 子组件
inject: ['yeye', 'data']
template: '<div>child component: {{data.value}}</div>'

new Vue, beforeCreate, created, beforeMount, mounted

没有el,就没有挂载beforeMount, mounted

若使用const app , app.$mount('#root') 就会执行

代码语言:javascript
复制
setInterval(() => {
 app.text = app.text += 1
},1000)

// 主动销毁
app.$destroy()

组件:

代码语言:javascript
复制
activated() {
 // 在组件
 console.log(this, 'activated')
}
deactivated() {
 // 在组件
 console.log(this, 'deactivated')
}

变化:

el:

代码语言:javascript
复制
undefined 'beforeCreate'
undefined 'created'
<div id="root"></div> "beforeMount"
<div>0</div> "mounted"

dom有关的放在mounted里面,数据可created,mounted

服务端渲染不会执行(因为服务端没有Dom环境,所有更本就没有这些内容),beforeMount,mounted, 会执行create,beforeCreate

生命周期

new Vue()

Init Events(事件已经ok了) & Lifecycle

beforeCreate(不要修改data里的数据)

Init injections & reactivity

created(数据操作)

Has 'el' option ? No when vm.$mount(el) is called

  • 判断是否有el // el: '#root'

YES Has 'template' option ?

  • 判断是否有 // template: ' <div> {{text}} </div> '

有template属性:

解析render

代码语言:javascript
复制
render(h) {
 console.log('render function invoked')
 return h('div', {}, this.text)
}

Waiting for update signal form WDS...

undefined "beforeCreate"
undefined "created"
 <div id="root"></div> "beforeMount"
render function invoked
 <div>0</div> "mounted"

Yes:有 Compile template into render function

No:没有 Compile el's outerHTML as template

beforeMount

Create vm.$el and replace 'el' with it

mounted

Mounted(实例创建完成)

  • when data changes (beforeUpdate) Virtual DOM re-render and patch (updated)
  • 走 when wm.$destroy() is called

beforeDestroy

Teardown watchers, child components and event listeners

destroyed

renderError

打包正式上线不会调用的,开发的时候会使用

代码语言:javascript
复制
renderError (h, err) {
 return h('div', {}, err.stack)
}

正式开发环境,收集线上的错误

代码语言:javascript
复制
errorCaptured() {
 <!-- 会向上冒泡 -->
}

vue里面的data绑定到template

watch监听到某个一数据的变化,指定某个操作,(服务器使用)

Vue的原生指令

Vue的组件 render function

代码语言:javascript
复制
render (createElement) {
 return createElement('comp-one', {
  ref: 'comp'
 }, [
  createElement('span', {
   ref: 'span'
  }, this.value)
 ])
}

render (createElement) {
 return createElement('div', {
  style: this.style,
  on: {
   click: () => { this.$emit('click') }
  }
 }, this.$slots.default)
}

Vue-Router && Vuex

代码语言:javascript
复制
import Router from 'vue-router'

import routers from './routes'

exports default () => {
 return new Router({
  routers,
  mode: 'history',
  // base: '/base/',
  linkActiveClass: 'active-link', // 子集
  linkExactActiveClass: 'exact-active-link', // 准确目标
  scrollBehavior (to, from, savedPosition) {
   if (savedPosition) {
    return savedPosition
   } else {
    return { x: 0, y: 0 }
   }
  },
  // parseQuery (query) {
  // },
  // stringifyQuery (obj) {
  // }
 })
}

transition

代码语言:javascript
复制
<transition name="fade">
 <router-view />
</transition>
代码语言:javascript
复制
this.$route
// path: '/app/:id',  to="/app/123"
fullPath: '/app/123'
hash: ""
matched: [{}]
meta: {title:''}
name: 'app'
params: {id: '123'}
path: '/app/123'
query: {}
代码语言:javascript
复制
// routes.js
{
 path: '/app/:id',
 props: true,
 component: Todo,
 name: 'app',
 meta: {
  title: 'this is app',
  description: 'asdasd'
 }
}

// todo.vue

props: ['id'],
mounted () {
 console.log(this.id)
}
代码语言:javascript
复制
{
 path: '/login',
 components: {
  default: Login,
  a: Todo
 }
}

Vue-router 导航守卫

代码语言:javascript
复制
import createRouter from './config/router'

Vue.use(VueRouter)

const router = createRouter()

router.beforeEach((to, from, next) => {
 // 做登录验证操作
 console.log('before each invoked')
 // next()
 if (to.fullPath === '/app') {
  next('/login') // 如果没有登录的话跳转到登录页面 next({ path: '/login' })
 } else {
  next() // 符合条件
 }
})

router.beforeResolve((to, from, next) => {
 console.log('before resolve invoked')
 next()
})

router.afterEach((to, from) => {
 console.log('after each invoked')
})
代码语言:javascript
复制
// routes.js

{
 path: '/app',
 beforeEnter (to, from, next) {
  console.log('app route before enter')
  next() // 只有当点击进入,才会调用
 }
}

// before each invoked
// app route before enter
// before resolve invoked
// after each invoked
代码语言:javascript
复制
// todo.vue
export default {
 beforeRouteEnter (to, from, next) {
  console.log('todo before enter')
  next()
 },
 
 beforeRouteUpdate (to, from, next) {
  console.log('todo update enter')
  next()
 }, 
 
 beforeRouteLeave (to, from, next) {
  console.log('todo leave enter')
  next()
 },

}

离开:

代码语言:javascript
复制
todo leave enter
before each invoked
before resolve invoked
after each invoked

进入:

代码语言:javascript
复制
before each invoked
app route before enter
todo before enter
before resolve invoked
after each invoked

Vuex集成

代码语言:javascript
复制
import Vuex from 'vuex'

const store = new Vuex.Store({
 state: {
  count: 0
 },
 mutations: {
  updateCount (state, num) {
   state.count = num
  }
 }
})

export default store

服务端渲染

代码语言:javascript
复制
import createRouter from './config/router'
import createStore from './store/store'

Vue.use(VueRouter)
Vue.use(Vuex)

const router = createRouter()
const store = createStore()

Vuex 中 state 和 getters

代码语言:javascript
复制
// store.js
import Vuex from 'vuex'
import defaultState from './state/state'
import mutations from './mutations/mutations'
import getters from './getters/getters'

export default () => {
 return new Vuex.Store({
  state: defaultState,
  mutations,
  getters
 })
}
代码语言:javascript
复制
// state/state.js
export default {
 count: 0,
 firstName: 'dada',
 lastName: 'dada'
}
代码语言:javascript
复制
// mutations/mutations.js
export default {
 updateCount (state, num) {
  state.count = num
 }
}

getters

代码语言:javascript
复制
// getters/getters.js =========== computed
export default {
 fullName(state) {
  return `${state.firstName} ${state.lastName}`
 }
}
代码语言:javascript
复制
// app.vue
computed: {
 count () {
  return this.$store.state.count
 },
 
 fullName () {
  return this.$store.getters.fullName
 }
}

快速使用

代码语言:javascript
复制
import {
 mapState,
 mapGetters
} from 'vuex'

computed: {
 // ...mapState(['count']),
 // ...mapState({
 //  counter: 'count'
 // }),
 ...mapState({
  counter: (state) => state.count
 }),
 ...mapGetters(['fullName'])
}

Vuex 中 mutation 和 action

代码语言:javascript
复制
// 开发环境 store.js
const isDev = process.env.NODE_ENV === 'development'

export default () => {
 return new Vuex.Store({
  strict: isDev,
  state: defaultState,
  mutations,
  getters
 })
}
代码语言:javascript
复制
// actions/actions.js
// dispatch 触发 actions 的
// 异步
export default {
 updateCountAsync (store, data) {
  setTimeout(() => {
   store.commit('updateCount', data.num)
  }, data.time)
 } 
}
代码语言:javascript
复制
// store.js
import Vuex from 'vuex'
import defaultState from './state/state'
import mutations from './mutations/mutations'
import getters from './getters/getters'
import actions from './actions/actions'

export default () => {
 return new Vuex.Store({
  state: defaultState,
  mutations,
  getters,
  actions
 })
}
代码语言:javascript
复制
import {
 mapState,
 mapGetters,
 mapActions,
 mapMutations
} from 'vuex'

mounted () {
 this.updateCountAsync({
  num: 5,
  time: 2000
 })
}

// mapActions mapMutations 操作
methods: {
 ...mapActions(['updateCountAsync']),
 ...mapMutations(['updateCount'])
}

Vuex 中的模块

代码语言:javascript
复制
// app.vue
mounted () {
 this['a/updateText']('123')
}

methods: {
 ...mapActions(['updateCountAsync']),
 ...mapMutations(['updateCount', 'a/updateText'])
}

computed: {
 ...mapState({
  counter: (state) => state.count,
  textA: state => state.a.text
 }),
 ...mapGetters(['fullName', 'a/textPlus'])
 textA () {
  return this.$store.state.a.text
 }
}

// store.js
modules {
 a: {
  namespaced: true
  state: {
   text: 1
  },
  mutations: {
   updateText (state, text) {
    console.log('a.state', state)
    state.text = text
   }
  },
  getters: {
   textPlus (state, getters, rootState) {
    return state.text + rootState.count + rootState.b.text
   }
  },
  actions: {
   add ({ state, commit, rootState }) {
    commit('updateText', rootState.count) // 全局找{ root: true }
    // commit('updateCount', rootState.count, { root: true }) // 全局找{ root: true }
   }
  }
 },
 
 b: {
  state: {
   text: 2
  },
  actions: {
   testAction ({ commit }) {
    commit('a/updateText', 'test text', { root: true })
   }
  }
 }
}

store.hotUpdate({})

Vuex 中的 API

代码语言:javascript
复制
// index.js
const router = createRouter()
const store = createStore()

store.registerModule('c', {
 state: {
  text: 3
 }
})

// 监听这个值的变化
store.watch((state) => state.count + 1, (newCount) => {
 console.log(newCount)
})

// 订阅
store.subscribe((mutation, state) => {
 console.log(mutation.type) // 调用哪个mutation
 console.log(mutation.payload) // mutation 接收的参数 传入的值
})

store.subscribeAction((action, state) => {
 console.log(action.type)
 console.log(action.payload)
})

store.unregisterModule('c')
代码语言:javascript
复制
// store.js
export default () => {
 const store = new Vuex.Store({
  strict: isDev,
  state: defaultState,
  mutations,
  getters,
  actions,
  plugins: [
   (store) => {
    console.log('my plugin invoked')
   }
  ]
 })
}

// my plugin invoked
// before each invoked
// before resolve invoked
// after each invoked

服务端渲染构建流程

访问服务端渲染页面:webpack server compiler -> nodejs server 3333端口

  1. 纯前端渲染:webpack dev server 8000 端口
  2. 访问服务端渲染页面:webpack server compiler server 创建 server bundle -> nodejs server 3333端
代码语言:javascript
复制
npm i vue -D // devDependencies

npm i vue -S // dependencies

npm i vue-server-renderer

npm i koa-router -S

npm i axios -S

server 服务端渲染

代码语言:javascript
复制
const koa = require('koa')
const app = new Koa()
const isDev = process.env.NODE_ENV = 'development'

dev-ssr.js

代码语言:javascript
复制
const Router = require('koa-router')
const axios = require('axios')
const MemoryFS = require('memory-fs')
const webpack = require('webpack')
const VueServerRenderer = require('vue-server-render')

组件开发

代码语言:javascript
复制
notification 通知
代码语言:javascript
复制
<template>
 <transition name="fade" @after-leave="afterLeave" @after-enter="afterEnter">
  <div class="notification" :style="style" v-show="visible" @mouseenter="clearTimer" @mouseleave="createTimer">
   <span class="content">{{content}}</span>
   <a class="btn" @click="handleClose">{{btn}}</a>
  </div>
 </transition>
</template>

<script>
export default {
 name: 'Notification',
 props: {
  content: {
   type: String,
   require: true
  },
  btn: {
   type: String,
   default: '关闭'
  }
 },
data() {
 return {
  visible: true 
 }
},
computed: {
 style () {
  return {}
 }
},
 methods: {
  handleClose (e) {
   e.preventDefault() // 默认事件阻止掉
   this.$emit("close")
  },
  afterLeave() {
   this.$emit("closed")
  },
  afterEnter() {
  },
  clearTimer(){},
  createTimer(){},
 }
}
</script>
代码语言:javascript
复制
// index.js
// 全局
import Notification from './notification.vue'
import notify from './function'

export default (Vue) => {
 Vue.component(Notification.name, Notification)
 Vue.prototype.$notify = notify
}
代码语言:javascript
复制
<notification content="test notify">
代码语言:javascript
复制
// func-notification.js
import Notification from './notification.vue'

export default {
 extends: Notification,
 computed: {
  style() {
   return {
    position:; 'fixed',
    right: '20px',
    bottom: `${this.verticalOffset}px`
   }
  }
 },
 mounted () {
  this.createTimer()
 },
 methods: {
  createTimer() {
   if (this.autoClose) {
    this.timer = setTimeout(() => {
     this.visible = false
    }, this.autoClose)
   }
  },
  clearTimer () {
   if (this.timer) {
    clearTImeout(this.timer)
   }
  },
  afterEnter() {
   this.height = this.$el.offsetHeight
  }
 },
beforeDestory () {
 this.clearTimer()
},
 data() {
  return {
   verticalOffset: 0,
   autoClose:  3000,
   height: 0,
   visible: false
  }
 }
}
代码语言:javascript
复制
// function.js
import Vue from 'vue'
import Component from './func-notification'

const NotificationConstructor = Vue.extend(Component)

const instances = []
let seed = 1 // 组件id的

const removeInstance = (instance) => {s
 if (!instance) return
 const len = instances.length
 const index = instances.findIndex(inst => instance.id === inst.id)

 instance.splice(index, 1)

 if (len <= 1) return
 const removeHeight = instance.vm.height
 for (let i = index; i < len - 1; i++) {
  instances[i].verticalOffset = parseInt(instances[i].verticalOffset) - removeHeight - 16
 }
}

const notify = (options) => {
 if (Vue.prototype.$isServer) return
 
 const {
  autoClose,
  ...rest
 } = options
 const instance = new NotificationConstructor({
  // propsData: options
  propsData: {
   ...rest
  },
  data: {
   autoClose: autoClose === undefined ? 3000 : autoClose
  }
 })

 const id = `notification_${seed++}`
 instance.id = id
 instance.vm = instance.$mount() // 节点有了,div😊有了

 document.body.appendChild(instance.vm.$el)
 instance.vm.visible = true

 let verticalOffset = 0
 instances.forEach(item => {
  verticalOffset += item.$el.offsetHeight + 16
 })
 verticalOffset += 16
 instance.verticalOffset = verticalOffset
 instances.push(instance)
 
 instance.vm.$on('close', () => {
  removeInstance(instance)
  document.body.removeChild(instance.vm.$el)
  instance.vm.$destroy()
 })
 instance.vm.$on('close', () => {
  instance.vm.visible = false
 })
 return instance.vm
}
代码语言:javascript
复制
// "precommit": "npm run lint-fix",

部署

https://github.com/webVueBlog/Vue-Vue-Router-Vuex-SSR

ok!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-02-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Vue-Vue-Router-Vuex-SSR
    • 服务端渲染
      • vue-loader配置
        • css module配置
          • 安装使用eslint和editorconfig以及precommit
            • webpack.config.base.js
              • Vue的一些点
                • VUE实例
                  • watch
                    • forceUpdate强制组件渲染一次
                      • vm.$nextTick([callback])
                        • vue生命周期
                          • 定义组件
                            • props
                              • 属性✍️
                                • slot-scope
                                  • provide inject
                                    • 生命周期
                                      • Vue的组件 render function
                                        • Vue-Router && Vuex
                                          • Vue-router 导航守卫
                                            • Vuex集成
                                              • Vuex 中 state 和 getters
                                                • getters
                                              • Vuex 中 mutation 和 action
                                                • Vuex 中的模块
                                                  • Vuex 中的 API
                                                    • 服务端渲染构建流程
                                                      • server 服务端渲染
                                                        • dev-ssr.js
                                                      • 组件开发
                                                        • 部署
                                                        领券
                                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档