20230721----重返学习-vue3实战项目初步

day-117-one-hundred-and-seventeen-20230721-vue3实战项目初步

vue3实战项目初步

创建vue3项目

清空多余的文件

  • 移除 assets 资源文件
  • 移除 components 目录下的所有组件
  • 删除 router 中的路由配置,页面的配置
  • 移除 stores 中的状态管理
  • 移除 views 下的所有页面组件
  • app.vue 中删除 没用的信息
  • main.js 中移除导入的 css 文件

安装项目需要的vscode插件

  • ESLint插件 保证代码格式是正确的,符合规范

支持pxtoRem

  1. 根据当前的屏幕大小/10来计算根节点的font-size

    pnpm i lib-flexible # 引入样式
    
    • src/main.js

      import 'lib-flexible'
      
  2. 把css文件中的px单位转成rem,具体转换比例看postcss.config.cjs。

    pnpm i postcss-pxtorem -D
    
    • postcss.config.cjs

      module.exports = {
        plugins: {
          'postcss-pxtorem': {
            // rootvalue是根据设计稿来计算的   vant在设计的时候 设计稿用的是375px
            rootValue: 37.5, // Vant 官方根字体大小是 37.5
            propList: ['*'] // 支持哪些属性的转换
          }
        }
      }
      
      • cjs表示是node环境,也可以用js,不过用js不好区分
  3. 报红,配置.eslintrc.cjs。是因为默认不支持node的CommonJS规范,只支持ESModule,而CommonJS规范2023年依旧广泛使用于node环境。而我们项目中,有一些配置相关的代码是在node环境下跑的,一般我们需要让这些在node环境下跑的代码后缀写成cjs,以便区分项目中的代码是跑在ESModule还是node。

    • .eslintrc.cjs 配置让项目环境支持node的CommonJS规范及浏览器的ES6Module规范。

      module.exports = {
        env: {
          browser: true,
          node: true
        },
      }
      
  • 测试配置是否成功了。
    • src/App.vue 看移动端宽度为414px时,背景是否占满整个屏幕。

      
      
      
      
      
      

组件按需导入

  1. 安装依赖

    pnpm i unplugin-vue-components #组件按需导入
    
    pnpm i vant #UI框架主体
    
  2. 配置插件需要的配置项

    • vite.config.js

      //实现组件内的动态导入,将vant-ui中的组件自动注册成全局组件。
      import Components from 'unplugin-vue-components/vite'
      import { VantResolver } from 'unplugin-vue-components/resolvers'
      export default defineConfig({
        plugins: [vue(), vueJsx(), Components({ resolvers: [VantResolver()] })]
      })
      
    • 测试普通组件

      • src/App.vue

        
        
  3. 配置全局属性或全局方法

    • 有些组件不能直接按需导入,需要配置到全局属性或全局指令或全局方法上

      • src/installVant.js 配置一些不能直接按需导入的全局属性及全局方法,以插件的形式来写,让相关功能的代码更统一在一起。

        import { showToast, showDialog, showNotify, showImagePreview, Lazyload } from 'vant'
        import 'vant/es/toast/style'
        import 'vant/es/dialog/style'
        import 'vant/es/image-preview/style'
        import 'vant/es/notify/style'
        
        export function installVant(app) {
          //属性
          app.config.globalProperties.$showToast = showToast
          app.config.globalProperties.$showDialog = showDialog
          app.config.globalProperties.$showNotify = showNotify
          app.config.globalProperties.$showImagePreview = showImagePreview
        
          // 指令
          app.use(Lazyload)
        
          // ... 配置vant中的指令和属性,增加相关配置
        }
        
      • src/main.js 导入上方配置好的全局属性及指令

        import { installVant } from './installVant'
        
        // 使用这行的前提是app已经创建,并且在app挂载到具体DOM前。
        installVant(app)
        
    • 测试是否全局属性已经按需导入了。

      • src/App.vue

        
        
  • 相关参考:
    1. vant-按需导入相关的文档

方法的自动导入

pnpm i unplugin-auto-import
  • vite.config.js
// 用于处理自动导入vue与vue-router与pinia中的方法。
import AutoImport from 'unplugin-auto-import/vite'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      eslintrc: {
        enabled: true // 开启后,生成eslint配置文件;
      }
    })
  ],
})
  • 运行一次之后,会生成一个/.eslintrc-auto-import.json文件,之后把plugins->AutoImport->eslintrc.enabled为false,防止每次都重新生成。

    • vite.config.js

      // 用于处理自动导入vue与vue-router与pinia中的方法。
      import AutoImport from 'unplugin-auto-import/vite'
      
      // https://vitejs.dev/config/
      export default defineConfig({
        plugins: [
          AutoImport({
            imports: ['vue', 'vue-router', 'pinia'],
            eslintrc: {
              enabled: false // 开启后,生成eslint配置文件;
            }
          })
        ],
      })
      
  • 用新生成文件的文件名,配置到.eslintrc.cjs中

    • .eslintrc.cjs

      /* eslint-env node */
      module.exports = {
        extends: [
          '.eslintrc-auto-import.json',
        ],
      }
      
  • 测试

    • src/App.vue

提交规范(没具体测试-好像是nvm与node混用导致的问题)

  • 让每次提交代码前都执行一次npm run lint;
  1. 每次提交前执行npm run lint;

  2. 使用husky插件

    git init  # git仓库初始化
    pnpm install husky -D  # 安装husky包
    npm pkg set scripts.prepare="husky install"  # 设置prepare命令脚本
    pnpm prepare  # 执行prepare命令
    npx husky add .husky/pre-commit "pnpm lint"  # 添加提交钩子
    

设置提交信息(没具体测试-好像是nvm与node混用导致的问题)

  • 对提交的注释格式要求更严格;
  1. 安装依赖

    pnpm install @commitlint/cli @commitlint/config-conventional -D
    
  2. 添加钩子命令

    npx husky add .husky/commit-msg 'npx --no-install commitlint --edit `echo "\$1"`'
    
  3. 设置插件

    • commitlint.config.cjs
    module.exports = {
      extends: ["@commitlint/config-conventional"],
    };
    
  4. 每次提交时,要加前缀,写的注释有要求。前缀样式如下

    build 主要⽬的是修改项⽬构建系统(例如 glup,webpack,rollup 的配置等)的提交
    chore 不属于以上类型的其他类型 ci 主要⽬的是修改项⽬继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle 等)的提交
    docs ⽂档更新
    feat 新功能、新特性
    fix 修改 bug
    perf 更改代码,以提⾼性能
    refactor 代码重构(重构,在不影响代码内部⾏为、功能下的代码修改)
    revert 恢复上⼀次提交
    style 不影响程序逻辑的代码修改(修改空⽩字符,格式 缩进,补全缺失的分号等,没有改变代码逻辑)
    test 测试⽤例新增、修改

修改ESLint的默认配置-配置多个单词的组件名

  • 允许单个单词的组件名。
    -.eslintrc.cjs
module.exports = {
  rules: {
    'vue/multi-word-component-names': 0
  },
}

基础路由

  • src/App.vue





  • src/views/Home.vue

  • src/views/Category.vue

  • src/views/Cart.vue

  • src/views/User.vue

  • src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', name: 'home', component: Home },
    { path: '/category', name: 'category', component: () => import('../views/Category.vue') },
    { path: '/cart', name: 'cart', component: () => import('../views/Cart.vue') },
    { path: '/user', name: 'user', component: () => import('../views/User.vue') }
  ]
})
export default router

默认注册某个文件夹下的vue文件为全局组件

  • vite.config.js

//实现组件内的动态导入,将vant-ui中的组件自动注册成全局组件。
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'


// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    Components({
      dirs: ['src/components'], // 希望components下的文件夹 自动变成全局组件
      resolvers: [VantResolver()]
    }),
  ],
})

字体库

  1. 选择图标,选择Symbol,得到iconfont.js。

  2. 去除fill=""的所有属性,去除连为默认背景色。

    • src/assets/iconfont.js

      /* eslint-disable */
      ;(window._iconfont_svg_string_4175143 =
        ''),
        (function (e) {
          var t = (t = document.getElementsByTagName('script'))[t.length - 1],
            l = t.getAttribute('data-injectcss'),
            t = t.getAttribute('data-disable-injectsvg')
          if (!t) {
            var c,
              a,
              o,
              i,
              q,
              n = function (t, l) {
                l.parentNode.insertBefore(t, l)
              }
            if (l && !e.__iconfont__svg__cssinject__) {
              e.__iconfont__svg__cssinject__ = !0
              try {
                document.write(
                  ''
                )
              } catch (t) {
                console && console.log(t)
              }
            }
            ;(c = function () {
              var t,
                l = document.createElement('div')
              ;(l.innerHTML = e._iconfont_svg_string_4175143),
                (l = l.getElementsByTagName('svg')[0]) &&
                  (l.setAttribute('aria-hidden', 'true'),
                  (l.style.position = 'absolute'),
                  (l.style.width = 0),
                  (l.style.height = 0),
                  (l.style.overflow = 'hidden'),
                  (l = l),
                  (t = document.body).firstChild ? n(l, t.firstChild) : t.appendChild(l))
            }),
              document.addEventListener
                ? ~['complete', 'loaded', 'interactive'].indexOf(document.readyState)
                  ? setTimeout(c, 0)
                  : ((a = function () {
                      document.removeEventListener('DOMContentLoaded', a, !1), c()
                    }),
                    document.addEventListener('DOMContentLoaded', a, !1))
                : document.attachEvent &&
                  ((o = c),
                  (i = e.document),
                  (q = !1),
                  d(),
                  (i.onreadystatechange = function () {
                    'complete' == i.readyState && ((i.onreadystatechange = null), s())
                  }))
          }
          function s() {
            q || ((q = !0), o())
          }
          function d() {
            try {
              i.documentElement.doScroll('left')
            } catch (t) {
              return void setTimeout(d, 50)
            }
            s()
          }
        })(window)
      
      
  3. src/main.js中导入图标所在的js文件

    • src/main.js

      import './assets/iconfont'
      
  4. 创建一个svg的通用样式组件。

    • src/components/SvgIcon.vue

      
      
      
      
      
  5. 在需要用到的地方里,使用该svg通用样式组件。

    • src/App.vue 注:SvgIcon组件由于在src/components中,之前已经把其配置为全局组件了。

      
      

修改UI框架的主题色

  1. 常见问题
  2. ConfigProvider 全局配置
  • src/assets/theme.css
:root:root {
  --van-primary-color: #1baeae;
}
  • src/main.js
import './assets/theme.css'
  • src/App.vue

使用less修改UI框架的主题色

pnpm install less
  • src/assets/var.less
@theme: #1baeae;
  • vite.config.js
export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        additionalData: '@import "/src/assets/var.less";'
      }
    }
  }
})
  • src/App.vue



设置导航不遮住内容

  • src/App.vue




  • src/components/NavBar.vue


首页上方假搜索

  • src/views/Home.vue


请求封装

pnpm install axios
  • src/utils/http.js
import axios from 'axios'
// 请求的时候可以做什么事? 1)拦截 携带token, 对响应状态码处理, 2)增加loading (增添一个队列)  3)接口可以保存取消的操作
class Http {
  constructor() {
    // 根据环境变量设置请求的路径
    this.baseURL = import.meta.env.DEV ? 'http://backend-api-01.newbee.ltd/api/v1' : '/'
    this.timeout = 5000
  }
  setInterceptor(instance) {
    instance.interceptors.request.use(
      (config) => {
        // 携带token来做处理
        return config
      },
      (err) => {
        return Promise.reject(err)
      }
    )
    instance.interceptors.response.use(
      (res) => {
        return res.data
      },
      (err) => {
        return Promise.reject(err)
      }
    )
  }
  request(options) {
    // 请求会实现拦截器
    const instance = axios.create() // 1.每次请求要创建一个新的实例
    this.setInterceptor(instance) // 2.设置拦截器
    // 发送请求参数
    return instance({
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout
    })
  }
  get(url, data) {
    return this.request({
      method: 'get',
      url,
      params: data
    })
  }
  post(url, data) {
    return this.request({
      method: 'post',
      url,
      data
    })
  }
}

export default new Http()
  • src/views/Home.vue

API的统一封装处理

  • src/api/index.js
import http from '@/utils/http.js'
const API_LIST = {
  queryIndexInfos: '/index-infos' // 首页的获取数据接口,都在这里
}
// 首页数据的获取,直接通过 api 这个文件来操作
export function queryIndexInfos() {
  return http.get(API_LIST.queryIndexInfos)
}
  • src/views/Home.vue



吸项效果

  1. 用一个例子包起,之后例子设置定位。
  • src/views/Home.vue

静态数据渲染

  • src/assets/category.js
export default [
  {
    name: '新蜂超市',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E8%B6%85%E5%B8%82%402x.png',
    categoryId: 100001
  },
  {
    name: '新蜂服饰',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E6%9C%8D%E9%A5%B0%402x.png',
    categoryId: 100003
  },
  {
    name: '全球购',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E5%85%A8%E7%90%83%E8%B4%AD%402x.png',
    categoryId: 100002
  },
  {
    name: '新蜂生鲜',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E7%94%9F%E9%B2%9C%402x.png',
    categoryId: 100004
  },
  {
    name: '新蜂到家',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E5%88%B0%E5%AE%B6%402x.png',
    categoryId: 100005
  },
  {
    name: '充值缴费',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E5%85%85%E5%80%BC%402x.png',
    categoryId: 100006
  },
  {
    name: '9.9元拼',
    imgUrl: 'https://s.yezgea02.com/1604041127880/9.9%402x.png',
    categoryId: 100007
  },
  {
    name: '领劵',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E9%A2%86%E5%88%B8%402x.png',
    categoryId: 100008
  },
  {
    name: '省钱',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E7%9C%81%E9%92%B1%402x.png',
    categoryId: 100009
  },
  {
    name: '全部',
    imgUrl: 'https://s.yezgea02.com/1604041127880/%E5%85%A8%E9%83%A8%402x.png',
    categoryId: 100010
  }
]
  • src/views/Home.vue


滚动后设置背景

  • src/views/Home.vue



点击出现弹框

  • src/views/Home.vue



骨架屏

  • src/views/Home.vue


图片设置懒加载

  1. image
  2. lazyload
  • 设置 lazy-load 属性来开启图片懒加载,需要搭配 Lazyload 组件使用。

  • src/main.js
import { createApp } from 'vue';
import { Lazyload } from 'vant';

const app = createApp();
app.use(Lazyload);

// 注册时可以配置额外的选项
app.use(Lazyload, {
  lazyComponent: true,
});

抽离静态组件

  • src/components/HomeGoodsItem.vue



  • src/views/Home.vue



处理图片-公共方法

  • src/utils/index.js
export function processURL(imgUrl) {
  // 如果你是以https开头的,直接采用即可
  // 否则则增加访问地址
  if (!/^https?:\/\//.test(imgUrl)) {
    return `http://backend-api-01.newbee.ltd${imgUrl}`
  }
  return imgUrl
}

export function addPrefix(money) {
  // 如果你是以https开头的,直接采用即可
  // 否则则增加访问地址
  return '¥' + money
}
  • src/components/HomeGoodsItem.vue




  • src/views/Home.vue



二次封装vant组件

  • src/components/TopBar.vue


  • src/views/Category.vue


配置@符在vue文件可以在vscode识别以便可以点击进对应的代码中

进阶参考

  1. 对应的项目-老师的
  2. 对应的项目-原版开源的
  3. 对应的项目-原版演示页面

你可能感兴趣的:(vue,重返学习,学习)