vue+vite学习笔记

创建vue+vite

npm init vite@latest esayblog-font-admin

运行该代码进行初始化vue+vite,npm install将node配置下载下来,

'vite' 不是内部或外部命令,也不是可运行的程序或批处理文件。

遇到这个问题是因为没有初始化 需要 npm install

 npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path C:\Users\lenovo\Desktop\museum/package.json
npm ERR! errno -4058
npm ERR! enoent ENOENT: no such file or directory, open 'C:\Users\lenovo\Desktop\museum\package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent
npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\lenovo\AppData\Local\npm-cache\_logs\2023-03-12T12_43_46_205Z-debug-0.log

安装element-plus库
在App组件中修改系统语言
项目框架结构视图:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
components:放置封装的组件,将封装的组件配置到main.js(就是全局组件,每个组价可以直接调用,不需要导入组件模块)

view:储存所有页面组件,

Framework:页面框架三大部分(Header,left-aside, el-main)

配置路由文件:新建router文件夹index.js

创建router

npm install vue-router@4 -save
const router = createRouter({
    routes,
    history: createWebHistory(),
    mode: 'hash',
    // base: process.env.BASE_URL,
})

配置路由路径

const routes = [
    {
        name: '登录',
        path: '/login',
        component: () =>
            import ('../view/Login.vue')
    },
    {
        name: '框架页',
        path: '/home',
        component: () =>
            import ('../view/Framework.vue'),
        redirect: '/blog/list',
        children: [{
                path: '/blog/list',
                name: '博客管理',
                component: () =>
                    import ("../view/blog/BlogList.vue")
            }]
      }
]

设置拦截,当路由没有设置cookies就跳回登录页面

// 设置拦截,如果没有登录或者cookies失效了就要退回登录页面
router.beforeEach((to, from, next) => {
    const userInfo = VueCookies.get("userInfo");
    if (!userInfo && to.path != '/login') {
        router.push('/login')
    }
    next();
})
export default router

配置代理

在vite.config.js中解决跨域问题,配置代理

export default defineConfig({
    plugins: [vue()],
    server: {
        // open: true,  自动打开浏览器
        hmr: true,
        port: 3001,
        proxy: {
            '^/api': {
                target: 'http://localhost:8081/', //目标代理接口地址
                secure: false,
                changeOrigin: true, //开启代理,在本地创建一个虚拟服务端
                pathRewrite: {
                    '^/api': 'api',
                },
            },
        },
    },
})

配置路径别名(解决导入文件路径的麻烦)

    resolve: {
        //配置路径别名
        alias: {
            '@': path.resolve(__dirname, './src')
        }
    },

解决vue打包内存过大问题:

 build: {
        chunkSizeWarningLimit: 1500,
        rollupOptions: {
            output: {
                manualChunks(id) {
                    if (id.includes('node_modules')) {
                        return id.toString().split('node_modules/')[1].split('/')[0].toString();
                    }
                }
            }
        }
    },

组件封装

封装表格:

   <el-table ref="dataTable"
              :data="dataSource.list || []"
              :height="tableHeight"
              :stripe="options.stripe"
              :border="options.border"
              header-row-class-name="table-header-row"
              highlight-current-row
              @row-click="handleRowClick"
              @selection-change="handleSelectionChange">
      </el-table>

data:数据列表

height:高度

stripe: 是否为斑马纹 table(默认没有)

border:是否带有纵向边框(默认没有)

 <!--selection选择框-->
      <el-table-column v-if="options.selectType && options.selectType == 'checkbox'"
                       type="selection"
                       width="50"
                       align="center"></el-table-column>
      <!--序号-->
      <el-table-column v-if="options.showIndex"
                       label="序号"
                       type="index"
                       width="60"
                       align="center"></el-table-column>

selection选择框:在表格前面添加checkbox,可以继续多选选择删除功能

   <!--数据列-->
      <template v-for="(column, index) in columns">
        <template v-if="column.scopedSlots">
          <el-table-column :key="index"
                           :prop="column.prop"
                           :label="column.label"
                           :align="column.align || 'left'"
                           :width="column.width">
            <template #default="scope">
              <slot :name="column.scopedSlots"
                    :index="scope.$index"
                    :row="scope.row">
              </slot>
            </template>
          </el-table-column>
        </template>
        <template v-else>
          <el-table-column :key="index"
                           :prop="column.prop"
                           :label="column.label"
                           :align="column.align || 'left'"
                           :width="column.width"
                           :fixed="column.fixed">
          </el-table-column>
        </template>
      </template>

判断数据是否重写数据列

如果有scopedSlots属性,就是自己定义template自己渲染数据列

设置分页:

 <div class="pagination"
      v-if="showPagination">
     <el-pagination v-if="dataSource.totalCount"
                     background
                     :total="dataSource.totalCount"
                     :page-sizes="[15, 30, 50, 100]"
                     :page-size="dataSource.pageSize"
                     :current-page.sync="dataSource.pageNo"
                     layout="total, sizes, prev, pager, next, jumper"
                     @size-change="handlePageSizeChange"
                     @current-change="handlePageNoChange"
                     style="text-align: center"></el-pagination>
    </div>
</div>
//切换每页大小
const handlePageSizeChange = (size) => {
  props.dataSource.pageSize = size;
  props.dataSource.pageNo = 1;
  props.fetch();
};
// 切换页码
const handlePageNoChange = (pageNo) => {
  props.dataSource.pageNo = pageNo;
  props.fetch();

封装对话框

<el-dialog
   :show-close="false"
   :draggable="true"
    v-model="(show)"
    :close-on-click-modal="false"
   :title="title"
   :showClose="showClose"
   class="cust-dialog"
   :top="top"
   :width="width"
   :showCancel="showCancel"
   @close="close"
  >
  <el-dialog>

对话框内容

 <div class="dialog-body">
    <slot>
    </slot>
  </div>

利用slot插槽将对话框内容留在中间部位

  <template v-if="buttons&&buttons.length>0|| showCancel">
      <div class="dialog-footer">
          <el-button link @click="close">
              取消
          </el-button>
     <el-button v-for="btn in buttons" :type="btn.type" @click="btn.click">
         {{btn.text}}
     </el-button>
     </div>
  </template>

遍历按钮个数

defineProps学习

在父组件内传递变量的时候,需要加冒号:,否则你就只是单纯的传递了一个字符串而已。

就是子组件定义defineProps属性,父组件直接在组件名后面写 :参数名=“参数”

子组件:(default为默认值)

const props=defineProps({
       title:{
         type:String
       },
      show: {
        type: Boolean,
        default: true
      },
       shwoClose:{
          type:Boolean,
          default:true,
       },
       top:{
           type:String,
           default:"50px"
       },
       width:{
           type:String,
           default:"30%"
       },
       buttons:{
           Array
       },
       showCancel:{
          type:Boolean,
          default:true,
       },
   })

父组件调用传参

  <Dialog
      :title="dialogConfig.title"
      :buttons="dialogConfig.buttons"
      @close="dialogConfig.show = false"
      :show="dialogConfig.show"
      width="500px"
    >
 <Dialog>

ref和reactive学习

特点: reactive的参数一般都是对象或者数组,能够将复杂的类型变成响应式数据

reactive的响应式是深层次的,底层本质是将传入的数据转换成Proxy对象

defineProperty很Proxy的区别

defineProperty只能单一的监听已有的属性的修改或者变化,无法检测到对象属性的新增或者删除而Proxy可以轻松实现

defineProperty无法监听属性值是数组类型的变化,而Proxy可以轻松实现

用reactive去声明简单数据类型,就会提示警告,提示这个值不能被reactive创建的
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
通过vue3源码可以看出来,当使用reactive定义数据时,会先进行判断是否是对象,是对象才会进行数据响应的处理,反之就直接被return出去了

因此reactive跟推荐用于对象或者数组的数据类型

ref

ref的参数一般是基本数据类型,也可以是对象类型

如果参数是对象类型,其实底层的本质还是reactive,系统会自动将ref转换为reactive,例如
ref(1) ===> reactive({value:1})

在模板中访问ref中的数据,系统会自动帮我们添加.value,在JS中访问ref中的数据,需要手动添加.value

ref的底层原理同reactive一样,都是Proxy

ref vs reactive

相同特点:底层原理都是用的Proxy

reactive参数一般接收对象和数组,是深层次的响应式,ref 一般接收简单数据类型,若 ref接收对象为参数,本质上会转换成reactive方法

在js中访问ref的值需要手动添加.value,访问reactive不需要

store学习

状态管理

在vue中每一个组件实例都是已经在管理它自己的响应式状态,我们以一个简单的计数器为例:

<script setup>
import { ref } from 'vue'
 
// 状态
const count = ref(0)
 
// 动作
function increment() {
  count.value++
}
</script>
 
<!-- 视图 -->
<template>{{ count }}</template>

它是一个独立单元,由状态,视图,交互三个部分组成

状态:驱动整个应用的数据源。

视图:在状态的一个声明式映射。

交互:状态根据用户在视图中的输入而作出响应变更的可能方式。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
当我们有多个组件共享一个共同的状态时,就没有怎么简单了:
多个视图都依赖同一份状态

来自不同视图的交互也可能需要更改同一份状态

用响应式API做简单状态管理

如果你有一部分需要在多个组件实例间共享,你可以使用reactive来创建一个响应式对象,并将它导入到多个组件中

当组件A和组件B需要同一个count变量,在改变A中count,B中的count也要改变

我们可以在store.js中定义一个响应式对象


import { reactive } from 'vue'

export const store =reactive({
  count:0,
    increment(){
      this.count++
    }
})
组件A
<template>
   <button @click="store.increment">
      FormA:{{store.count}}
   </button>
 </template>
<script setup>
import { store } from './store.js'
</script>
组件B
<template>
   <button @click="store.increment">
   FormB:{{store.count}}
  </button>    
</template>
<script setup>
import { store } from './store.js'
</script>

vuex是专门为Vue.js设计的状态管理库,以利用vue.js的细粒度数据响应机制响应来进行高效的状态更新
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
更新页面处理

先获取请求地址(url),和请求参数(params),定义这些参数

let { url, params, dataType = 'form', showLoading = 'true' } = config;
    let contentType = contentTypeForm;
    if (dataType === "json") {
        contentType = contentTypeJson;
    } else if (dataType === "file") {
        contentType = contentTypeFile;
        let param = new FormData();
        for (let key in params) {
            param.append(key, params[key]);
        }
        params = param;
    }

判断请求参数类型如果是json不需要遍历添加,如果是file,需要遍历添加

用axios请求

 const instantce = axios.create({
        baseURL: '/api',   基础地址
        timeout: 10 * 1000,  超时设置
        headers: {           
            'Content-Type': contentType,   请求头
            'X-Requested-With': 'XMLHttpRequest',
        }
    })

请求拦截器

 let loading = null;
    instantce.interceptors.request.use(
        (config) => {
            if (showLoading) {
                loading = ElLoading.service({  加载
                    lock: true,
                    text: '加载中......',
                    background: 'rgba(0, 0, 0, 0.7)',
                })
            }
            return config;  请求前做一个加载弹框
        }, 
        (error) => {
            if (showLoading && loading) {
                loading.close();
            }
            message.error("发送请求失败");
            return Promise.reject("发送请求失败");  请求失败返回失败信息
        }
    )

响应拦截器


  instantce.interceptors.response.use(
        (response) => {   获取响应后数据,取消加载框
            if (showLoading && loading) {
                loading.close();
            }
            const responseData = response.data;
            if (responseData.status == "error") {
                if (config.errorCallback) {
                    config.errorCallback();
                }
                return Promise.reject(responseData.info);
            } else {
                if (responseData.code == 200) {
                    return responseData;
                } else if (responseData.code == 901) {
                    setTimeout(() => {
                        router.push("/login")
                    }, 2000);
                    return Promise.reject("登录超时");
                }
            }
        },
        (error) => {
            console.log(error);
            if (showLoading && loading) {
                loading.close();
            }
            return Promise.reject("网络异常");
        }
    )

defineExpose用法

在vue3中,父组件可以创建一个ref(null) 然后将赋值的元素写在当前子组件上即可,在需要的时候,通过定义的响应式变量即可获取,获取后可取得当前子组件内部,dom以及当前子组件内部的变量方法等,并使用子组件=内部方法,但是有时候获取的时候返回没有什么信息。

重点:使用script setup 语法的组件是默认关闭的,通过ref或者$parent链获取的组件的公开实例,不会暴露任何在script setup中声明的绑定

方法: 为了在script setup语法组件中明确要暴露出去的属性,使用defineExpose编译器将需要暴露出去的变量和方法放入暴露出去就可以

子组件使用defineExpose暴露实例

<script setup>
import { ref } from 'vue';
    const demo=ref('测试用例')
    const  handleVal=()=>{
        demo.value="已改变"
    }
   将需要暴露出去的数据与方法都可以暴露出去
    defineExpose({
        demo,
        handleVal,
    })
</script>
<template>
  <div class="inner">
      {{demo}}
      <button @click="handleVal">改值</button>
  </div>
</template>
<style lang="scss" scoped>
</style>

父组件

<template>
  <div class="container">
      <h-demo ref="childDom" />
      <button @click="getChild"></button>
  </div>
</template>
<script setup>
import Hdemo from '@/components/Hdemo';
import { ref, } from 'vue';
const childDom=ref(null)
onMounted(() => {
  const getChild = () =>{
    console.log(childDom.value)
     打印当前子组件暴露出来的数据
    childDom.value.handleVal()
    // 执行子组件方法
  }
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值