【vue】vue3学习笔记(一)

前言

  • 通过制作管理系统学习vue3

流程

  • 需要4.5以上
npm i -g @vue/cli

#查看版本
vue -V
vue create vue3-element-admin
  • 选手动配置
  • 除了pwa和下面2个测试,其他都选上。
  • Vue版本选3.x
  • 关于TypeScript两个选项 都选no
  • 路由模式 使用Hash模式
  • 选择 dart-sass 预处理器
  • 选择 eslint配置 Standard
  • 选择lint to save
  • 生独立配置文件
  • 最后一项不保存为preset 选择n
  • 回车创建
vue add element-plus
  • 安装会提示让你选是否要按需导入,语言包。
  • 会自动创建src/plugins/element.js
  • 将src/plugins/element.js修改为src/plugins/element.ts
  • 文件改为:
import {
    
     createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import installElementPlus from './plugins/element'

const app = createApp(App)

app
.use(store)
.use(router)
.use(installElementPlus)
.mount('#app')
  • 入口修改:
import {
    
     createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import installElementPlus from './plugins/element'

const app = createApp(App)

app
.use(store)
.use(router)
.use(installElementPlus)
.mount('#app')
  • app.vue导入btn试一下:
<template>
  <el-button type="primary">el-button</el-button>
</template>

<script>

export default {
      
      
  name: 'App'
}
</script>

<style>
#app {
      
      
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
  • 看见elemet 的btn即可。
  • 修改eslint规则,关闭一些:
module.exports = {
    
    
  root: true,
  env: {
    
    
    node: true
  },
  extends: [
    'plugin:vue/vue3-essential',
    '@vue/standard',
    '@vue/typescript/recommended'
  ],
  parserOptions: {
    
    
    ecmaVersion: 2020
  },
  rules: {
    
    
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'space-before-function-paren': 'off',
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/interface-name-prefixed': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/ban-ts-ignore': 'off'
  }
}
  • 初始化css
npm install --save normalize.css
  • 导入:
import 'normalize.css/normalize.css'
  • 创建layout布局
  • src/layout/index.vue:
<template>
  <div class="app-wrapper">
    <div class="sidebar-container">sidebar</div>
    <div class="main-container">
      <div class="header">
        <div class="navbar">navbar</div>
        <div class="tags-view">tagsview</div>
      </div>
      <div class="app-main">
        <h2>app main</h2>
        <router-view></router-view>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
  .app-wrapper {
      
      
    display: flex;
    width: 100%;
    height: 100%;
    .main-container {
      
      
      flex: 1;
      display: flex;
      flex-direction: column;
      .header {
      
      
        background: cyan;
        .navbar {
      
      
          height: 50px;
          background: #1890ff;
        }
        .tags-view {
      
      
          height: 34px;
          background: #12efff;
        }
      }
      .app-main {
      
      
        /* 50= navbar  50  如果有tagsview + 34  */
        min-height: calc(100vh - 84px);
        background: red;
      }
    }
  }
</style>
  • src/views/创建dashboard:
<template>
  <div>
    <h1>Dashboard page</h1>
  </div>
</template>

<script>
export default {
      
      
  name: 'Dashboard'
}
</script>
  • 配置路由 router/index.ts
import {
    
     createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import Layout from '@/layout/index.vue'

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
    
    
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import(/* webpackChunkName: "dashboard" */ '@/views/dashboard/index.vue'),
        meta: {
    
    
          title: 'Dashboard'
        }
      }
    ]
  }
]

const router = createRouter({
    
    
  history: createWebHashHistory(),
  routes
})

export default router
  • src下创建 src/styles目录存放全局样式文件
  • style下的sidebar.scss
#app {
    
    
  .sidebar-container {
    
    
    width: $sideBarWidth !important;
    height: 100%;
    background-color: pink;
  }
}
  • variable.scss
// base color
$blue:#324157;
$light-blue:#3A71A8;
$red:#C03639;
$pink: #E65D6E;
$green: #30B08F;
$tiffany: #4AB7BD;
$yellow:#FEC171;
$panGreen: #30B08F;

// sidebar
$menuText:#bfcbd9;
$menuActiveText:#409EFF;
$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951

$menuBg:#304156;
$menuHover:#263445;

$subMenuBg:#1f2d3d;
$subMenuHover:#001528;

$sideBarWidth: 210px;

// The :export directive is the magic sauce for webpack
// https://mattferderer.com/use-sass-variables-in-typescript-and-javascript
:export {
    
    
  menuText: $menuText;
  menuActiveText: $menuActiveText;
  subMenuActiveText: $subMenuActiveText;
  menuBg: $menuBg;
  menuHover: $menuHover;
  subMenuBg: $subMenuBg;
  subMenuHover: $subMenuHover;
  sideBarWidth: $sideBarWidth;
}
  • 这个导出写法上面有个链接,告诉你可以在js ts中使用他们。
  • ts中写个声明文件:
export interface ScssVariables {
    
    
  menuText: string;
  menuActiveText: string;
  subMenuActiveText: string;
  menuBg: string;
  menuHover: string;
  subMenuBg: string;
  subMenuHover: string;
  sideBarWidth: string;
}

export const variables: ScssVariables

export default variables
  • main中引入:
import '@/styles/index.scss'
  • app.vue中引入路由:
<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
export default {
      
      
  name: 'App'
}
</script>

<style>
#app {
      
      
  height: 100%;
}
</style>
  • 此时,能看见布局ok即可。
  • 配置SVG
  • 先准备好一些svg
  • 安装loder
npm install svg-sprite-loader -D
  • vue.config.js中加入配置:
'use strict'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path')

const resolve = dir => path.join(__dirname, dir)

function chainWebpack(config) {
    
    
 
  // 在已有的svg loader配置中 排除掉对src/icons里svg进行转换
  config.module
    .rule('svg')
    .exclude.add(resolve('src/icons')) // 排除掉src/icons目录
    .end()
  // svg icon工作原理 https://segmentfault.com/a/1190000015367490
  // 配置svg-sprite-loader
  config.module
    .rule('icons')
    .test(/\.svg$/)
    .include.add(resolve('src/icons')) // 指定src/icons要处理svg的文件目录
    .end()
    .use('svg-sprite-loader')
    .loader('svg-sprite-loader') // 用svg-sprite-loader解析
    .options({
    
    
      symbolId: 'icon-[name]' // symbol id命名格式 icon-图标名称
    })
    .end()
}

module.exports = {
    
    
  chainWebpack,
  devServer: {
    
    
    port: 8080,
    open: true,
    overlay: {
    
    
      warnings: false,
      errors: true
    }
  }
}
  • 制作svg组件:
<template>
  <!-- 如果iconClass是带协议的图标链接 则通过style属性方式渲染-->
  <div
    class="svg-icon svg-external-icon"
    v-if="isExt"
    :style="styleExternalIcon"
    v-bind="$attrs"
  ></div>
  <!-- SVG icon 通过名称使用 -->
  <svg v-else :class="svgClass" aria-hidden="true" v-bind="$attrs">
    <!-- 
       SVG中的use元素可以调用其他SVG文件的元素,<use xlink:href="#symbolId"></use>
    -->
    <use :xlink:href="iconName" />
  </svg>
</template>

<script lang="ts">
import {
      
       isExternal } from '@/utils/validate'
import {
      
       computed, defineComponent } from 'vue'

/**
 * v-bind="$attrs" 组件$attrs属性透传绑定到元素上
 * vue3.0中$lietens已被移除 现在事件监听器是 $attrs 的一部分
 * 文档说明:
 * https://v3.cn.vuejs.org/guide/migration/listeners-removed.html#_3-x-%E8%AF%AD%E6%B3%95
 */
export default defineComponent({
      
      
  name: 'SvgIcon',
  inheritAttrs: false, // 组件上的$attrs不自动添加到组件根元素上 默认添加到组件根元素上
  props: {
      
      
    iconClass: {
      
      
      type: String,
      require: true
    },
    className: {
      
      
      // 我们也可以自定义类名
      type: String,
      default: ''
    }
  },
  setup(props) {
      
      
    // 是否是带协议的图片链接
    const isExt = computed(() => isExternal(props.iconClass || ''))
    // 拼接成symbolId 在loader配置中指定了symbolId格式 icon-图标名称
    const iconName = computed(() => `#icon-${ 
        props.iconClass}`)
    // 添加类名 props.className外部传入自定义类名
    const svgClass = computed(() =>
      props.className ? `svg-icon ${ 
        props.className}` : 'svg-icon'
    )
    // 如果iconClass是带协议的图标链接 则通过style css属性方式渲染
    const styleExternalIcon = computed(() => ({
      
      
      mask: `url(${ 
        props.iconClass}) no-repeat 50% 50%`,
      '-webkit-mask': `url(${ 
        props.iconClass}) no-repeat 50% 50%`
    }))

    return {
      
      
      isExt,
      iconName,
      svgClass,
      styleExternalIcon
    }
  }
})
</script>

<style scoped>
.svg-icon {
      
      
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
.svg-external-icon {
      
      
  background-color: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>

  • 注册进app
import {
    
     App } from 'vue'
import SvgIcon from '@/components/SvgIcon/index.vue'

const req = require.context('./svg', false, /\.svg$/)
const requireAll = (requireContext: ReturnType<typeof require.context>) => requireContext.keys().map(requireContext)
requireAll(req)

export default (app: App): void => {
    
    
  app.component('svg-icon', SvgIcon)
}

  • 入口导入:
import {
    
     createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 初始化css
import 'normalize.css/normalize.css'
// element-plus
import installElementPlus from './plugins/element'
// 全局 css
import '@/styles/index.scss'
// svg icons
import initSvgIcon from '@/icons/index'

const app = createApp(App)

app
  .use(store)
  .use(router)
  .use(installElementPlus)
  .use(initSvgIcon)
  .mount('#app')
  • 在dashboard引用下:
<template>
  <div>
    <h1>Dashboard page</h1>
    <svg-icon icon-class="bug"></svg-icon>
    <!-- icon-class svg图标名称 class-name 额外的自定义类名 @click绑定事件 -->
    <svg-icon icon-class="404" class-name="custom-class" @click="sayHi"></svg-icon>

  </div>
</template>

<script>
export default {
      
      
  name: 'Dashboard',
  setup() {
      
      
    const sayHi = () => {
      
      
      alert('hi svg')
    }
    return {
      
      
      sayHi
    }
  }
}
</script>

<style lang="scss">
  .custom-class {
      
       // 自定义样式404
    font-size: 200px;
    color: green;
  }
</style>
  • 有svg出现即ok。
  • 下面使用svgo进行svg优化:
  • 全局安装svgo https://github.com/svg/svgo
  • 写入yml配置:
# replace default config

# multipass: true
# full: true

plugins:
  # - name
  #
  # or:
  # - name: false
  # - name: true
  #
  # or:
  # - name:
  #     param1: 1
  #     param2: 2

  - removeAttrs:
      attrs:
        - 'fill'
        - 'fill-rule'
  • 增加命令即可:
  "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
  • 全局命令挂载
  • 注意,全局命令的声明不要写道shims.vue.d.ts里,因为里面如果有import之类整个项目编译会有问题。
  • 所以声明放到main.ts下:
declare module '@vue/runtime-core' {
    
    
  interface ComponentCustomProperties {
    
    
    $message: typeof ElMessage
    $notify: typeof ElNotification
    $confirm: typeof ElMessageBox.confirm
    $alert: typeof ElMessageBox.alert
    $prompt: typeof ElMessageBox.prompt
  }
}

  • app.config.globalProperties代替原来的vue.prototype
import {
    
     App } from 'vue'
import {
    
    
  locale,
  ElButton,
  ElMessage,
  ElNotification,
  ElMessageBox
} from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
// Element Plus 组件内部默认使用英语
// https://element-plus.gitee.io/#/zh-CN/component/i18n
import lang from 'element-plus/lib/locale/lang/zh-cn'
// Element Plus 直接使用了 Day.js 项目的时间日期国际化设置, 并且会自动全局设置已经导入的 Day.js 国际化配置。
import 'dayjs/locale/zh-cn'

// $ELEMENT size属性类型
export type Size = 'default' | 'medium' | 'small' | 'mini'

export default (app: App): void => {
    
    
  locale(lang)

  // 按需导入组件列表
  const components = [ElButton, ElMessage, ElNotification, ElMessageBox]

  components.forEach(component => {
    
    
    app.component(component.name, component)
  })

  // Vue.prototype 替换为 config.globalProperties
  // 文档说明 https://v3.cn.vuejs.org/guide/migration/global-api.html#vue-prototype-%E6%9B%BF%E6%8D%A2%E4%B8%BA-config-globalproperties
  app.config.globalProperties.$message = ElMessage
  app.config.globalProperties.$notify = ElNotification
  app.config.globalProperties.$confirm = ElMessageBox.confirm
  app.config.globalProperties.$alert = ElMessageBox.alert
  app.config.globalProperties.$prompt = ElMessageBox.prompt

  // element-plus全局配置
  // 说明文档:https://element-plus.gitee.io/#/zh-CN/component/quickstart#quan-ju-pei-zhi
  // 该对象目前支持 size 与 zIndex 字段。size 用于改变组件的默认尺寸 small,zIndex 设置弹框的初始 z-index(默认值:2000)。
  app.config.globalProperties.$ELEMENT = {
    
    
    size: 'medium'
    // zIndex: 2000 弹框zIndex默认值:2000
  }
}
  • 用dashboard试验下:
<template>
  <div>
    <h1>Dashboard page</h1>
    <svg-icon icon-class="bug"></svg-icon>
    <!-- icon-class svg图标名称 class-name 额外的自定义类名 @click绑定事件 -->
    <svg-icon
      icon-class="404"
      class-name="custom-class"
      @click="sayHi"
    ></svg-icon>
  </div>
</template>

<script lang="ts">
import {
    
     getCurrentInstance, defineComponent } from 'vue'

export default defineComponent({
    
    
  name: 'Dashboard',
  setup() {
    
    
    // 无法使用ctx 使用proxy来代替
    // https://blog.csdn.net/qq_39115469/article/details/113817592
    const {
    
     proxy } = getCurrentInstance()!
    const sayHi = () => {
    
    
      proxy?.$message.success('恭喜你,这是一条成功消息')
    }
    return {
    
    
      sayHi
    }
  }
})
</script>

<style lang="scss">
.custom-class {
    
    
  // 自定义样式404
  font-size: 200px;
  color: green;
}
</style>

点击svg弹出提示则正常。

猜你喜欢

转载自blog.csdn.net/yehuozhili/article/details/117044534