文章目录

1.毕设开始

vue create erp-pc
vue create erp-app

2.流程

erp-pc

npm install @vue/composition-api --save

3.遇到的问题

1.Vue 报错:Module build failed: Error: Cannot find module ‘node-sass’

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJmS00Gr-1594275653995)(https://images2017.cnblogs.com/blog/1163744/201711/1163744-20171106145442497-1646668115.png)]

解决方法:

输入命令:cnpm install node-sass@latest

2.Vue日常踩坑日常 —— 在index.html中引入静态文件不生效问题

本文针对的是Vue小白,不喜勿喷,谢谢

出现该问题的标志如下

控制台warning(Resource interpreted as Stylesheet but transferred with MIME type text/html

img

出现的原因及解决办法

第一种可能出现原因就是引入的静态文件在src文件夹内,这种的解决办法就是把资源引入静态资源的目录static

第二种可能出现的原因就是有单独的静态资源目录但是名字不叫static,这种的解决办法更改配置文件,把对应的几个配置文件内的static更改为你自己所创建的静态资源目录,由于网上大多教程所改的地方都不够完全,还是会出现该问题,所以这也是今天为什么要写这篇文章的原因。

需要更改的有3个文件,分别是config文件夹下的index.js,build文件夹下的webpack.dev.conf.jswebpack.prod.conf.js

假如你的静态资源文件夹叫public,和src文件夹同级,需要修改的如下

1. index.js

Copy  dev: {
    assetsSubDirectory: 'public',//原本是static,现在改为public
    assetsPublicPath: `/${name}/`,
    
    ...
    
  build: {
    index: path.resolve(__dirname, '../dist/index.html'),
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'public',//原本是static,现在改为public

2. webpack.dev.conf.js

Copy    // copy custom static assets
    new CopyWebpackPlugin([
      {
        //下面原本是static,现在改为public
        from: path.resolve(__dirname, '../public'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])

3. webpack.prod.conf.js

Copy    // copy custom static assets
    new CopyWebpackPlugin([
      {
        //下面原本是static,现在改为public
        from: path.resolve(__dirname, '../public'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ])

Ps:有任何问题欢迎在评论区探讨

3.vue项目中 favicon.ico不能正确显示的问题

首先将favicon.ico图片放在根目录下,通过以下两种方法使其显示正确。
方法一:修改index.html文件

<link rel="shortcut icon" type="image/x-icon" href="favicon.ico"/>

方法二:修改webpack配置文件

1、找到build下的webpack.dev.conf.js文件

new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true,
      favicon: path.resolve('favicon.ico') // 增加
    }),

2、找到build下的webpack.prod.conf.js文件

 new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      favicon: path.resolve('favicon.ico'), //新增
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        ...
    }),

修改配置文件后重启npm run dev,大功告成。
注意:如果打包发布到线上,会导致ico图标不显示的问题,是因为执行npm run build打包后只有static文件夹和index.html文件,找不到根目录下的ico图标,解决办法:把ico图标放到static文件夹下就OK啦。

4.Module build failed: TypeError: this.getResolve is not a function at Object.loader 安装node-sass运行报错

vue安装node-sass编译报错
安装node-scss报错
安装node-scss报错
在搭建vue脚手架 或者是在vue项目中,想使用sass的功能,

npm install node-sass --save-dev //安装node-sass
npm install sass-loader --save-dev //安装sass-loader
npm install style-loader --save-dev //安装style-loader
安装完成后,运行时出现了错误

Modele build failed: TypeError: this.getResolve is not a function at Object.loader…
这是因为当前sass的版本太高,webpack编译时出现了错误,这个时候只需要换成低版本的就行,下面说一下修改方法,很简单,如下,找到package.json文件,里面的 "sass-loader"的版本更换掉 就行了。

我本地是将 “sass-loader”: “^8.0.0”,更换成了 “sass-loader”: “^7.3.1”,
这时候重新跑项目,就运行成功了。

也可以先卸载当前版本,然后安装指定的版本

卸载当前版本 npm uninstall sass-loader
安装 npm install sass-loader@7.3.1 --save-dev

5.sass 在vue中报错
vue文件:
<style scoped lang="scss">
@import '~scss_vars';
</style>
错误提示:
      File to import not found or unreadable: ~scss_vars.
Parent style sheet: stdin
      in D:\vue\mock\src\views\Home.vue (line 79, column 1)
      
package.json:
"file-loader": "^1.1.5",
    "mockjs": "^1.0.1-beta3",
    "node-sass": "^4.6.0",
    "sass-loader": "^6.0.6",

如果你要这么写就需要在webpack里配置resolve 如果你用的vue-cli的话就是在webpack.base.config.js里配置

resolve: {
    extensions: ['.js', '.vue', '.json', '.scss'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'scss': path.resolve(__dirname, '../src/style'),
    }
  }

这个样式根路径,如果指定某个文件就要在别名里指定即可

已经说了 这个scss resolve的是根路径,而你要引入的是styles里面的scss_vars.scss文件 那么你就要@import ‘~scss/scss_vars’

4.Vue3.0新特性

0. 基础要求
  1. 了解常见的 ES6 新特性
    • ES6 的导入导出语法
    • 解构赋值
    • 箭头函数
    • etc…
  2. 了解 vue 2.x 的基本使用
    • 组件
    • 常用的指令
    • 生命周期函数
    • computed、watch、ref 等
1. 相关资源
  • 【知乎 - Vue Function-based API RFC】https://zhuanlan.zhihu.com/p/68477600
  • 【github - vuejs/composition-api】https://github.com/vuejs/composition-api
  • 【github - composition-api/CHANGELOG.md】https://github.com/vuejs/composition-api/blob/master/CHANGELOG.md
  • 【开源中国 - 尤雨溪公布 Vue 3.0 开发路线:将从头开始重写 3.0】https://www.oschina.net/news/100515/plans-for-the-next-iteration-of-vue-js
2. 初始化项目
  1. 安装 vue-cli3

    npm install -g @vue/cli
    # OR
    yarn global add @vue/cli
    
  2. 创建项目

    vue create my-project
    # OR
    vue ui
    
  3. 在项目中安装 composition-api 体验 vue3 新特性

    npm install @vue/composition-api --save
    # OR
    yarn add @vue/composition-api
    
  4. 在使用任何 @vue/composition-api 提供的能力前,必须先通过 Vue.use() 进行安装

    import Vue from 'vue'
    import VueCompositionApi from '@vue/composition-api'
    
    Vue.use(VueCompositionApi)
    

    安装插件后,您就可以使用新的 Composition API 来开发组件了。

3. setup

setup() 函数是 vue3 中,专门为组件提供的新属性。它为我们使用 vue3 的 Composition API 新特性提供了统一的入口。

3.1 执行时机

setup 函数会在 beforeCreate 之后、created 之前执行

相当于 created, 只会执行一次

3.2 接收 props 数据
  1. props 中定义当前组件允许外界传递过来的参数名称:

    props: {
        p1: String
    }
    
  2. 通过 setup 函数的第一个形参,接收 props 数据:

    setup(props) {
        console.log(props.p1)
    }
    
3.3 context

setup 函数的第二个形参是一个上下文对象,这个上下文对象中包含了一些有用的属性,这些属性在 vue 2.x 中需要通过 this 才能访问到,在 vue 3.x 中,它们的访问方式如下:

const MyComponent = {
  setup(props, context) {
    context.attrs
    context.slots
    context.parent
    context.root
    context.emit
    context.refs
  }
}

注意:在 setup() 函数中无法访问到 this

4. reactive

reactive() 函数接收一个普通对象,返回一个响应式的数据对象。

4.1 基本语法

等价于 vue 2.x 中的 Vue.observable() 函数,vue 3.x 中提供了 reactive() 函数,用来创建响应式的数据对象,基本代码示例如下:

import { reactive } from '@vue/composition-api'

// 创建响应式数据对象,得到的 state 类似于 vue 2.x 中 data() 返回的响应式对象
const state = reactive({ count: 0 })
4.2 定义响应式数据供 template 使用
  1. 按需导入 reactive 函数:

    import { reactive } from '@vue/composition-api'
    
  2. setup() 函数中调用 reactive() 函数,创建响应式数据对象:

    setup() {
         // 创建响应式数据对象
     const state = reactive({count: 0})
    
         // setup 函数中将响应式数据对象 return 出去,供 template 使用
     return state
    }
    
  3. template 中访问响应式数据:

    <p>当前的 count 值为:{{count}}</p>
    
5. ref
5.1 基本语法

ref() 函数用来根据给定的值创建一个响应式数据对象ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性:

import { ref } from '@vue/composition-api'

// 创建响应式数据对象 count,初始值为 0
const count = ref(0)

// 如果要访问 ref() 创建出来的响应式数据对象的值,必须通过 .value 属性才可以
console.log(count.value) // 输出 0
// 让 count 的值 +1
count.value++
// 再次打印 count 的值
console.log(count.value) // 输出 1
5.2 在 template 中访问 ref 创建的响应式数据
  1. setup() 中创建响应式数据:

    import { ref } from '@vue/composition-api'
    
    setup() {
     const count = ref(0)
    
         return {
             count,
             name: ref('zs')
         }
    }
    
  2. template 中访问响应式数据:

    <template>
     <p>{{count}} --- {{name}}</p>
    </template>
    
5.3 在 reactive 对象中访问 ref 创建的响应式数据

当把 ref() 创建出来的响应式数据对象,挂载到 reactive() 上时,会自动把响应式数据对象展开为原始的值,不需通过 .value 就可以直接被访问,例如:

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 输出 0
state.count++            // 此处不需要通过 .value 就能直接访问原始值
console.log(count)       // 输出 1

注意:新的 ref 会覆盖旧的 ref,示例代码如下:

// 创建 ref 并挂载到 reactive 中
const c1 = ref(0)
const state = reactive({
  c1
})

// 再次创建 ref,命名为 c2
const c2 = ref(9)
// 将 旧 ref c1 替换为 新 ref c2
state.c1 = c2
state.c1++

console.log(state.c1) // 输出 10
console.log(c2.value) // 输出 10
console.log(c1.value) // 输出 0
6. isRef

isRef() 用来判断某个值是否为 ref() 创建出来的对象;应用场景:当需要展开某个可能为 ref() 创建出来的值的时候,例如:

import { isRef } from '@vue/composition-api'

const unwrapped = isRef(foo) ? foo.value : foo
7. toRefs

toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据,最常见的应用场景如下:

import { toRefs } from '@vue/composition-api'

setup() {
    // 定义响应式数据对象
    const state = reactive({
      count: 0
    })
    
    // 定义页面上可用的事件处理函数
    const increment = () => {
      state.count++
    }
    
    // 在 setup 中返回一个对象供页面使用
    // 这个对象中可以包含响应式的数据,也可以包含事件处理函数
    return {
      // 将 state 上的每个属性,都转化为 ref 形式的响应式数据
      ...toRefs(state),
      // 自增的事件处理函数
      increment
    }
}

页面上可以直接访问 setup() 中 return 出来的响应式数据:

<template>
  <div>
    <p>当前的count值为:{{count}}</p>
    <button @click="increment">+1</button>
  </div>
</template>
8. computed

computed() 用来创建计算属性,computed() 函数的返回值是一个 ref 的实例。使用 computed 之前需要按需导入:

import { computed } from '@vue/composition-api'
8.1 创建只读的计算属性

在调用 computed() 函数期间,传入一个 function 函数,可以得到一个只读的计算属性,示例代码如下:

// 创建一个 ref 响应式数据
const count = ref(1)

// 根据 count 的值,创建一个响应式的计算属性 plusOne
// 它会根据依赖的 ref 自动计算并返回一个新的 ref
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 输出 2
plusOne.value++            // error
8.2 创建可读可写的计算属性

在调用 computed() 函数期间,传入一个包含 getset 函数的对象,可以得到一个可读可写的计算属性,示例代码如下:

// 创建一个 ref 响应式数据
const count = ref(1)

// 创建一个 computed 计算属性
const plusOne = computed({
  // 取值函数
  get: () => count.value + 1,
  // 赋值函数
  set: val => { count.value = val - 1 }
})

// 为计算属性赋值的操作,会触发 set 函数
plusOne.value = 9
// 触发 set 函数后,count 的值会被更新
console.log(count.value) // 输出 8
9. watch

watch() 函数用来监视某些数据项的变化,从而触发某些特定的操作,使用之前需要按需导入:

import { watch } from '@vue/composition-api'
9.1 基本用法
const count = ref(0)

// 定义 watch,只要 count 值变化,就会触发 watch 回调
// watch 会在创建时会自动调用一次
watch(() => console.log(count.value))
// 输出 0

setTimeout(() => {
  count.value++
  // 输出 1
}, 1000)
9.2 监视指定的数据源

监视 reactive 类型的数据源:

// 定义数据源
const state = reactive({ count: 0 })
// 监视 state.count 这个数据节点的变化
watch(() => state.count, (count, prevCount) => { /* ... */ })

监视 ref 类型的数据源:

// 定义数据源
const count = ref(0)
// 指定要监视的数据源
watch(count, (count, prevCount) => { /* ... */ })
9.3 监视多个数据源

监视 reactive 类型的数据源:

const state = reactive({ count: 0, name: 'zs' })

watch(
  [() => state.count, () => state.name],    // Object.values(toRefs(state)),
  ([count, name], [prevCount, prevName]) => {
    console.log(count)         // 新的 count 值
    console.log(name)          // 新的 name 值
    console.log('------------')
    console.log(prevCount)     // 旧的 count 值
    console.log(prevName)      // 新的 name 值
  },
  {
    lazy: true // 在 watch 被创建的时候,不执行回调函数中的代码
  }
)

setTimeout(() => {
  state.count++
  state.name = 'ls'
}, 1000)

监视 ref 类型的数据源:

const count = ref(0)
const name = ref('zs')

watch(
  [count, name], // 需要被监视的多个 ref 数据源
  ([count, name], [prevCount, prevName]) => {
    console.log(count)
    console.log(name)
    console.log('-------------')
    console.log(prevCount)
    console.log(prevName)
  },
  {
    lazy: true
  }
)

setTimeout(() => {
  count.value++
  name.value = 'xiaomaolv'
}, 1000)
9.4 清除监视

setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可,语法如下:

// 创建监视,并得到 停止函数
const stop = watch(() => { /* ... */ })

// 调用停止函数,清除对应的监视
stop()
9.5 在 watch 中清除无效的异步任务

有时候,当被 watch 监视的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除那些无效的异步任务,此时,watch 回调函数中提供了一个 cleanup registrator function 来执行清除的工作。这个清除函数会在如下情况下被调用:

  • watch 被重复执行了
  • watch 被强制 stop

Template 中的代码示例如下

/* template 中的代码 */
<input type="text" v-model="keywords" />

Script 中的代码示例如下

// 定义响应式数据 keywords   
const keywords = ref('')

// 异步任务:打印用户输入的关键词
const asyncPrint = val => {
  // 延时 1 秒后打印
  return setTimeout(() => {
    console.log(val)
  }, 1000)
}

// 定义 watch 监听
watch(
  keywords,
  (keywords, prevKeywords, onCleanup) => {
    // 执行异步任务,并得到关闭异步任务的 timerId
    const timerId = asyncPrint(keywords)

    // 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务
    onCleanup(() => clearTimeout(timerId))
  },
  // watch 刚被创建的时候不执行
  { lazy: true }
)

// 把 template 中需要的数据 return 出去
return {
  keywords
}
10. LifeCycle Hooks

新版的生命周期函数,可以按需导入到组件中,且只能在 setup() 函数中使用,代码示例如下:

import { onMounted, onUpdated, onUnmounted } from '@vue/composition-api'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}

下面的列表,是 vue 2.x 的生命周期函数与新版 Composition API 之间的映射关系:

  • beforeCreate -> use setup()
  • created -> use setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured
11. provide & inject

provide()inject() 可以实现嵌套组件之间的数据传递。这两个函数只能在 setup() 函数中使用。父级组件中使用 provide() 函数向下传递数据;子级组件中使用 inject() 获取上层传递过来的数据。

11.1 共享普通数据

App.vue 根组件:

<template>
  <div id="app">
    <h1>App 根组件</h1>
    <hr />
    <LevelOne />
  </div>
</template>

<script>
import LevelOne from './components/LevelOne'
// 1. 按需导入 provide
import { provide } from '@vue/composition-api'

export default {
  name: 'app',
  setup() {
    // 2. App 根组件作为父级组件,通过 provide 函数向子级组件共享数据(不限层级)
    //    provide('要共享的数据名称', 被共享的数据)
    provide('globalColor', 'red')
  },
  components: {
    LevelOne
  }
}
</script>

LevelOne.vue 组件:

<template>
  <div>
    <!-- 4. 通过属性绑定,为标签设置字体颜色 -->
    <h3 :style="{color: themeColor}">Level One</h3>
    <hr />
    <LevelTwo />
  </div>
</template>

<script>
import LevelTwo from './LevelTwo'
// 1. 按需导入 inject
import { inject } from '@vue/composition-api'

export default {
  setup() {
    // 2. 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
    const themeColor = inject('globalColor')
    
    // 3. 把接收到的共享数据 return 给 Template 使用
    return {
      themeColor
    }
  },
  components: {
    LevelTwo
  }
}
</script>

LevelTwo.vue 组件:

<template>
  <div>
    <!-- 4. 通过属性绑定,为标签设置字体颜色 -->
    <h5 :style="{color: themeColor}">Level Two</h5>
  </div>
</template>

<script>
// 1. 按需导入 inject
import { inject } from '@vue/composition-api'

export default {
  setup() {
    // 2. 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
    const themeColor = inject('globalColor')

    // 3. 把接收到的共享数据 return 给 Template 使用
    return {
      themeColor
    }
  }
}
</script>
11.2 共享 ref 响应式数据

如下代码实现了点按钮切换主题颜色的功能,主要修改了 App.vue 组件中的代码,LevelOne.vueLevelTwo.vue 中的代码不受任何改变:

<template>
  <div id="app">
    <h1>App 根组件</h1>
    
    <!-- 点击 App.vue 中的按钮,切换子组件中文字的颜色 -->
    <button @click="themeColor='red'">红色</button>
    <button @click="themeColor='blue'">蓝色</button>
    <button @click="themeColor='orange'">橘黄色</button>

    <hr />
    <LevelOne />
  </div>
</template>

<script>
import LevelOne from './components/LevelOne'
import { provide, ref } from '@vue/composition-api'

export default {
  name: 'app',
  setup() {
    // 定义 ref 响应式数据
    const themeColor = ref('red')
    
    // 把 ref 数据通过 provide 提供的子组件使用
    provide('globalColor', themeColor)
    
    // setup 中 return 数据供当前组件的 Template 使用
    return {
      themeColor
    }
  },
  components: {
    LevelOne
  }
}
</script>
12. template refs

通过 ref() 还可以引用页面上的元素或组件。

12.1 元素的引用

示例代码如下:

<template>
  <div>
    <h3 ref="h3Ref">TemplateRefOne</h3>
  </div>
</template>

<script>
import { ref, onMounted } from '@vue/composition-api'

export default {
  setup() {
    // 创建一个 DOM 引用
    const h3Ref = ref(null)

    // 在 DOM 首次加载完毕之后,才能获取到元素的引用
    onMounted(() => {
      // 为 dom 元素设置字体颜色
      // h3Ref.value 是原生DOM对象
      h3Ref.value.style.color = 'red'
    })

    // 把创建的引用 return 出去
    return {
      h3Ref
    }
  }
}
</script>
12.2 组件的引用

TemplateRefOne.vue 中的示例代码如下:

<template>
  <div>
    <h3>TemplateRefOne</h3>
    
    <!-- 4. 点击按钮展示子组件的 count 值 -->
    <button @click="showNumber">获取TemplateRefTwo中的count值</button>

    <hr />
    <!-- 3. 为组件添加 ref 引用 -->
    <TemplateRefTwo ref="comRef" />
  </div>
</template>

<script>
import { ref } from '@vue/composition-api'
import TemplateRefTwo from './TemplateRefTwo'

export default {
  setup() {
    // 1. 创建一个组件的 ref 引用
    const comRef = ref(null)

    // 5. 展示子组件中 count 的值
    const showNumber = () => {
      console.log(comRef.value.count)
    }

    // 2. 把创建的引用 return 出去
    return {
      comRef,
      showNumber
    }
  },
  components: {
    TemplateRefTwo
  }
}
</script>

TemplateRefTwo.vue 中的示例代码:

<template>
  <div>
    <h5>TemplateRefTwo --- {{count}}</h5>
    <!-- 3. 点击按钮,让 count 值自增 +1 -->
    <button @click="count+=1">+1</button>
  </div>
</template>

<script>
import { ref } from '@vue/composition-api'

export default {
  setup() {
    // 1. 定义响应式的数据
    const count = ref(0)
    
    // 2. 把响应式数据 return 给 Template 使用
    return {
      count
    }
  }
}
</script>
13. createComponent

这个函数不是必须的,除非你想要完美结合 TypeScript 提供的类型推断来进行项目的开发。

这个函数仅仅提供了类型推断,方便在结合 TypeScript 书写代码时,能为 setup() 中的 props 提供完整的类型推断。

import { createComponent } from 'vue'

export default createComponent({
  props: {
    foo: String
  },
  setup(props) {
    props.foo // <- type: string
  }
})

5.Vue3.0 生命周期函数

在组件化的框架中,比如Angular、React或Vue,都为组件定义了生命周期这个概念,每个组件实例在被创建时都要经过一系列的初始化过程,例如:需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时,在这个过程中也会运行一些叫做生命周期钩子的函数,它们提供给用户在组件的不同阶段添加自己的代码的机会。

使用过Vue2.x的朋友肯定对它的生命周期钩子很熟悉了,因为在实际的开发过程中我们多多少少会用到他们,比如 created、mounted、destoryed等等。而在Vue3.0中,Vue2.x Options API形式的生命周期钩子函数和新的Composition API都可以使用,来看个示例代码就明白了:

const { onMounted } = Vue

const MyComp = {
    
    // Options API
    mounted() {
        console.log('>>>>>> mounted 1')
    },
    
    setup() {
        // Composition API
        onMounted(() => {
            console.log('++++++ mounted 2')
        })
    }
}

两种形式的生命周期函数可以共存(当然实际使用的时候最好只选用一种),它们都会被执行。Composition API形式的生命周期函数都是在 setup 方法中被调用注册。

最后,在实际的开发过程中,请注意一下Options API形式的组件生命周期钩子和Composition API之间的实际对应关系:

  • beforeCreate -> 请使用 setup()
  • created -> 请使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

6.Vue Composition API

最近,Vue官方发布了 Composition API RFC。有关于Vue3.0 Function Base组件相关讨论正如火如荼。虽然Vue3.0还未发布,但是 Vue官方发布了关于 Composition API的官方插件,使广大用户可以在Vue2.x中享受 Function Base 带来的新体验。下面我会在一个简单的demo中介绍Composition API使用方法。

安装
yarn add @vue/compostion-api

等待composition-api包安装完成后,我们在项目入口文件中加入Composition API。

import Vue from "vue"
import CompositionApi from "@vue/compostion-apai"
Vue.use(CompositionApi)
setup

按照官方给出的说法,setup函数是一个新的Vue组件选项,是用于在组件中使用Composition API的入口。

setup函数在组件初始化了props之后,created之前调用,这时候我们才能通过setup来传递props。

ref

ref是CompositionAPI引入的新概念。作用是使访问响应式的变量不依赖于实例的this。如果使用了ref,我们访问响应式的变量时使用.value而不是从this中获取。

import {ref} from "@vue/compostion-apai"
export default {
    setup() {
        const count = ref(0)//count初始值为0,会相应变化
        count.value = 10;//设置count值为10,使用.value形式
        return {
            count//必须将count return 回去
        }
    }
}
生命周期函数

可以使用导入的onXXX的形式注册生命周期函数,举个例子:

import {onCreated,onMounted} from "@vue/compostion-apai"
export default {
    setup() {
        onCreated(()=>{
            console.log('created被触发')    
        })
        onMounted(()=>{
            console.log('mounted被触发')
        })
        ///...其他类似
    }
}
methods

在Composition API中,我们使用普通的函数定义方法,这样可以最大程度的增加复用性。例如:

<template>
<button @click="add">click me!</button>
</template>
<script>
export default {
    setup() {
        function add() {
            console.log('add被触发')
        }
        return {
            add//必须将函数return
        }
    }
}
</script>
props

定义props和原来的方式一样,props会通过参数的形式传入到setup函数中:

export default {
    props:{
        name:String
    },
    setup(props) {
        console.log(props.name)
    }
}
computed

计算属性可以使用Composition API提供的computed函数进行定义:

import {computed,ref} from "@vue/compostion-apai"
export default {
    setup() {
        const a = ref(0)
        const b = ref(1);
        const total = computed(()=>a.value+b.value)
        return {
            a,
            b,
            totla
        }
    }
}

这样我们就定义号了一个计算属性totaltotal.value = a.value + b.value

watch

在组件中添加watch监听我们可以采用Compostion API提供的watch函数实现:

import {watch,ref} from "@vue/compostion-apai"
export default {
    setup() {
        const count = ref(100);
        watch(()=>count.vlaue,()=>{
            console.log('count数值发生变化了')
        })
        const.value = 200;
        return {
            count
        }
    }
}
获取Vue组件实例或者dom节点

在Composition API中使用ref特性获取组件实例或者dom节点,举个例子说明:

<template>
<div>
    <hello-world ref="helloWold"></hello-world>
    <button ref="btn"></button>
</div>
</template>
import {ref} from "@vue/composition-api"
export default {
    setup() {
        const helloWorld = ref(null);//helloworld组件实例
        const btn = ref(null);//button dom节点对象
        return {
            btn,
            helloWorld
        }
    }
}
综合例子
<template>
    <div>
        <button @click="increment" ref="btn">{{titleCount}}</button>
    </div>
</template>

<script>

import {onMounted,computed,ref,watch} from "@vue/composition-api"
export default {
  props:{
    prefix:String
  },
  setup(props) {
    const btn = ref(null);
    const count = ref(0);
    const title = ref(`${props.prefix},vue composition api`);
    const titleCount = computed(()=>title.value +':'+ count.value)
    onMounted(()=>{
      alert('mounted')
    })
    watch(()=>count.value,()=>alert('count改变了:'+count.value))
    function increment() {
      // eslint-disable-next-line no-console
      count.value++;
      // eslint-disable-next-line no-console
      console.log(btn.value);
    }

    return {
      btn,
      increment,
      title,
      count,
      titleCount
    }
  }
}
</script>

7.CSS中!important 作用

1、这个属性可以让浏览器优选执行这个语句,加上!importanrt可以覆盖父级的样式。具体的效果可以用一个案例演示,首先新建一个html文件,先新建一个外围div,在在里面设置两个子div,给父div设置class属性parent,第二个子div增加import的class属性:

img

2、然后在上方的style中设置样式,给div设置宽度和高度,并给它背景颜色;给import类设置背景色,并加入!important 属性就会覆盖父容器的背景颜色:

img

3、最后来到浏览器中,就可以看到第二个div的背景颜色被覆盖了:

img

CSS中“!important”可以使它之前的样式优先执行,比如说这样:
body {
color : red !important;
}
body {
color : black;
}
颜色color出现了两次,而加上“!important”的“color : red;”总会得到优先执行,浏览器将对另一个“color : black;”视而不见。
之所以会说得复杂,是因为“IE 6.0一直都不支持这个语法,而其他的浏览器都支持。因此可以利用这一点来分别给IE和其他浏览器不同的样式定义”,我认为这只不过是一个小技巧罢了。

8.Normalize.css

Normalize.css是一种CSS reset的替代方案。它在默认的HTML元素样式上提供了跨浏览器的高度一致性。相比于传统的CSS reset,Normalize.css是一种现代的、为HTML5准备的优质替代方案(https://necolas.github.io/normalize.css/7.0.0/normalize.css)

创造normalize.css有下面这几个目的

保护有用的浏览器默认样式而不是完全去掉它们

一般化的样式:为大部分HTML元素提供

修复浏览器自身的bug并保证各浏览器的一致性

优化CSS可用性:用一些小技巧

解释代码:用注释和详细的文档来

Normalize.css支持包括手机浏览器在内的超多浏览器,同时对HTML5元素、排版、列表、嵌入的内容、表单和表格都进行了一般化。尽管这个项目基于一般化的原则,但我们还是在合适的地方使用了更实用的默认值。

Normalize vs Reset

\1. Normalize.css 保护了有价值的默认值

​ Reset通过为几乎所有的元素施加默认样式,强行使得元素有相同的视觉效果。相比之下,Normalize.css保持了许多默认的浏览器样式。这就意味着你不用再为所有公共的排版元素重新设置样式。当一个元素在不同的浏览器中有不同的默认值时,Normalize.css会力求让这些样式保持一致并尽可能与现代标准相符合。

2. Normalize.css 修复了浏览器的bug

​ 它修复了常见的桌面端和移动端浏览器的bug。这往往超出了Reset所能做到的范畴。关于这一点,Normalize.css修复的问题包含了HTML5元素的显示设置、预格式化文字的font-size问题、在IE9中SVG的溢出、许多出现在各浏览器和操作系统中的与表单相关的bug。

3、Normalize.css 不会让你的调试工具变的杂乱

​ 使用Reset最让人困扰的地方莫过于在浏览器调试工具中大段大段的继承链,如下图所示。在Normalize.css中就不会有这样的问题,因为在我们的准则中对多选择器的使用时非常谨慎的,我们仅会有目的地对目标元素设置样式。

4、Normalize.css 是模块化的

​ 这个项目已经被拆分为多个相关却又独立的部分,这使得你能够很容易也很清楚地知道哪些元素被设置了特定的值。因此这能让你自己选择性地移除掉某些永远不会用到部分(比如表单的一般化)。

5. Normalize.css 拥有详细的文档

​ Normalize.css的代码基于详细而全面的跨浏览器研究与测试。这个文件中拥有详细的代码说明并在Github Wiki中有进一步的说明。这意味着你可以找到每一行代码具体完成了什么工作、为什么要写这句代码、浏览器之间的差异,并且你可以更容易地进行自己的测试。

如何使用 normalize.css

首先,安装或从Github下载Normalize.css,接下来有两种主要途径去使用它。

策略一:将normalize.css作为你自己项目的基础CSS,自定义样式值以满足设计师的需求。

策略二:引入normalize.css源码并在此基础上构建,在必要的时候用你自己写的CSS覆盖默认值。

vue框架中如何使用

NPM
npm install --save normalize.css

mianimport 'normalize.css'  如果报错
npm install css-loader style-loader

normalize.css 是什么?

     它是css样式初始化的插件。

    在实际的开发中,我们经常会将浏览器默认的样式重置,比如如下办法:
  • {
    margin: 0;
    padding: 0;
    border:0;
    }
    通过上述办法可以重置样式,但是太过于简单粗暴,*是通配符,需要把所有的标签都遍历一遍,当网站较大时,样式比较多,这样写就大大的加强了网站运行的负载,会使网站加载的时候需要很长一段时间,因此不建议使用。与此同时,normalize.css就诞生了,它与许多CSS重置不同,保留了有用的默认值,更正了常见浏览器不一致性等错误。

normalize.css 怎么使用?

1、安装

npm install --save normalize.css
2、引入

import ‘normalize.css/normalize.css’

9.vue中监控元素大小变化element-resize-detector

导入npm install element-resize-detector
1.可以浏览器统一导入 如放到index.html

<script src="node_modules/element-resize-detector/dist/element-resize-detector.min.js"></script>

2.也可以在vue中导入
如在

monted(){
  var elementResizeDetectorMaker = require("element-resize-detector");//导入
  // 创建实例
  var erd = elementResizeDetectorMaker();
  // 创建实例带参
  var erdUltraFast = elementResizeDetectorMaker({
                strategy: "scroll", //<- For ultra performance.
                callOnAdd: true,
                debug: true
            });
  //监听id为test的元素 大小变化
  erd.listenTo(document.getElementById("test"), function(element) {
    var width = element.offsetWidth;
    var height = element.offsetHeight;
    console.log("Size: " + width + "x" + height);
  });
}

3.erd实例方法
RemoveListener(element,listener)
从元素中移除侦听器。

4.RemoveAllListener(element)
从元素中移除所有侦听器,但不完全删除检测器。如果以后可能会添加侦听器,并且不希望检测器再次初始化,请使用此函数。

5.uninstall(element)
完全删除检测器和所有侦听器。

10.element-ui弹出层置于遮罩层下面问题

有时页面结构复杂,多个嵌套MessageBox之类的情况时,MessageBox 弹框会出现置于遮罩层下面的问题,需要添加

:modal-append-to-body="false"属性。
注:该属性可以将弹出层的遮罩层在body内生成。

代码实例

11.在项目中,遇到vue父子组件的传参问题,父组件传参给子组件,子组件在created和mounted等生命周期中获取不到,也无法使用父组件传递过来的参数。

在项目中,遇到vue父子组件的传参问题,父组件传参给子组件,子组件在created和mounted等生命周期中获取不到,也无法使用父组件传递过来的参数。
父组件传参,一般分为两种情况:

1、父组中的原始数据 (即定义在父组件data中的原始数据),传输给子组件使用,子组件在生命周期中是可以获取并使用的。

2、父组件传给子组件的参数,是从接口中获取到的, 可能遇到的问题就是,如果在组件的created或mounted生命周期中定义要给方法,在子组件渲染时就获取父组件的传值,并进行新数据的获取并进行页面渲染,但此时父组件请求的数据可能还没有完全返回,子组件就开始使用此数据,该数据自然不存在,打印在控制台中的父组件穿给子组件的参数为undefined,因此就会出现父组件传参不及时,子组件使用数据较早,造成数据无法使用。

解决改问题的方法:

1、在子组件的created或者mounted等较早执行的生命周期中使用setTimeout定时器,来延缓执行生命周期中的函数, 使父组件从接口中获取的参数,有充分的时间传输,在子组件使用时,等待父组件的参数到来再执行,就不会出现拿不到父组件的传参问题了。

//----------------------------子组件--------------------------------
export default {
    props:['fatherData'],
    data(){
        return {
                }
},
created(){
    this.setTime() // 延时1.5秒调用方法,等待父组件的传参返回
},
methods:{
    setTime(){ // 定时器,延时调用方法
      let win = this
      setTimeout(function(){
          win.getData() // 获取数据
      },1500)  
    },
    getData(){
        // 使用父组件传过来的数据,进行新的请求
        this.$api.getData(this.fatherData).then(res=>{
            console.log(res)
        }
    }
}
}
// 该方法虽然可以解决上述问题,但延时调用影响了页面的响应速度,
// 不是一个很好的解决办法(不推荐)

该方法虽然可以解决上述问题,但延时调用影响了页面的响应速度,
不是一个很好的解决办法(不推荐)

2、另一种方法为watch侦听:
使用vue的监听方法,监听父组件的传参,一旦侦听到数据返回,便立即调用子组件的方法进行数据获取,相对于第一种方法,更加灵活和快速。

12.Axios 各种请求方式传递参数格式

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
axios.request(config)
//原始的Axios请求方式
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  },
  timeout: 1000,
  ...//其他相关配置
});
axios.get(url[, config])
axios.get('demo/url', {
    params: {
        id: 123,
        name: 'Henry',
    },
   timeout: 1000,
  ...//其他相关配置
})
axios.delete(url[, config])
//如果服务端将参数作为java对象来封装接受
axios.delete('demo/url', {
    data: {
        id: 123,
        name: 'Henry',
    },
     timeout: 1000,
    ...//其他相关配置
})
//如果服务端将参数作为url参数来接受,则请求的url为:www.demo/url?a=1&b=2形式
axios.delete('demo/url', {
    params: {
        id: 123,
        name: 'Henry',
    },
     timeout: 1000,
    ...//其他相关配置
})
axios.post(url[, data[, config]])
axios.post('demo/url', {
    id: 123,
    name: 'Henry',
},{
   timeout: 1000,
    ...//其他相关配置
})
axios.put(url[, data[, config]])
axios.put('demo/url', {
    id: 123,
    name: 'Henry',
},{
   timeout: 1000,
    ...//其他相关配置
})
axios.patch(url[, data[, config]])
axios.patch('demo/url', {
    id: 123,
    name: 'Henry',
},{
   timeout: 1000,
    ...//其他相关配置
})

总结: 通过以上案例可以看出,get delete请求方式中,第一个参数为请求的url地址,第二个参数为请求的一些配置项,需要传递给后端的参数包含在配置项的data或者params属性中,而post put patch请求则第一个参数为url地址,第二个参数是需要入参的json数据,第三个参数是入参以外的其他配置项。

Logo

前往低代码交流专区

更多推荐