vue

Vue2

局部刷新

1,app.vue 中局部刷新

<template>
    <div id="app">
        <router-view v-if="isShow" />
    </div>
</template>

<script>
export default {
  name: "App",
  provide() {
    //父组件中通过provide来提供变量,在子组件中通过inject接受。
    return {
      reload: this.reload,
    };
  },
  data() {
    return {
      //自定义参数
      isShow: true,
    };
  },
  methods: {
    // 刷新
    reload() {
      //reload方法首先将isShow设置为false,将router-view通过if判断取消
      this.isShow = false;
      // Vue.nextTick用于延迟执行一段代码,它接受2个参数(回调函数和执行回调函数的上下文环境),如果没有提供回调函数,那么将返回promise对象。
      this.$nextTick(function () {
        //     在页面更新后再将isShow设置为true
        this.isShow = true;
      });
    },
  },
};
</script>

2,使用得页面中

export default {
    inject:['reload'],//  此处引入在app中定义的reload方法
    name: 'Home'
}

3,接口调用事件

methods: {
      // 右键事件
      delLink(item,e){
        delLink(item.id).then(res=>{
          if (res.data.code === 200) {
            console.log("->",res.data);
            this.$message({
              message: '删除成功',
              type: 'success'
            });
            this.reload(); // 主要   主要   主要
          }
        }).catch(res=>{console.log("delLink->error")})
},

路由history打包后页面空白

vue默认路由模式为 hash,一般打包不会有问题,hash模式url中会带有 #

history模式下,如果项目直接放在根目录下,打包也不会出现什么问题,非根目录的项目需要加上base 的路径

vue router配置

const router = new VueRouter({
  mode: "history",//路由模式
  base: '/justMe',//项目路径
  routes: routes,
  scrollBehavior(to, from, savedPosition) {
    return {x: 0, y: 0}
  }
})

nginx配置

项目指定打包放在根目录

location / {
 try_files $uri $uri/ /index.html;
}

如果不是根目录访问

location /justMe {
   root /apps/justMe;
   index index.html index.htm;
   #error_page 404 /history/index.html;
   if (!-e $request_filename) {
      rewrite ^/(.*) /justMe/index.html last;
      break;
   }
}

数据更新,页面未更新

解决:页面dom元素加 if 判断

V-if="tableList.length>0"

Vuex

https://v3.vuex.vuejs.org/zh/api/

vuex

state :提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,相似与data

Action:提交提交到mutation中,而不是直接变更状态,总是接受 context 作为第一个参数,payload 作为第二个参数(可选), 可用es6语法解构出 commit,state,dispatch等属性

Mutation:更改 Vuex 的 store 中的状态的唯一方法,总是接受 state 作为第一个参数,payload 作为第二个参数(可选)

const store = new Vuex.Store({
  state: {
    count: 1,
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
  },
  mutations: {
    increment (state,num) {
      // 变更状态
      state.count+=num
    }
  },
  actions: {
    //这里是用了es6解构语法 从context中解构出commit
    increment ({ commit },num) {
      commit('increment',num)
    }
  }
})

组件 –dispatch–> Actions

Action中触发其他Module的Action
dispatch('tagsView/delAllViews', null, { root: true })
触发tagsView这个Module中Actions的delAllViews方法

组件中 :this.$store.dispatch("increment")

Actions–commit–>Mutation

组件中
this.$store.commit("increment",10)
调用了Mutation的increment方法

Mutation –更改–> State

 state.count+=num

State —-渲染—-> 组件

this.$store.state.count

Getters

接受 state 作为其第一个参数, 也可以接受其他 getter 作为第二个参数

组件中使用:
this.$store.getters.doneTodosCount

Module

Vu3+TS

节流

1,新建preventReClick.ts

import { App } from 'vue'

export const setupPreventReClickDirective = (app: App<Element>) => {
    //自定义指令 preventReClick
    app.directive(
        'preventReClick',
        //自定义节流操作
        {
            mounted(el: any, binding: any) {
                el.addEventListener('click', () => {
                    if (!el.disabled) {
                        console.log('true')
                        el.disabled = true
                        setTimeout(() => {
                            el.disabled = false
                        }, binding.value || 500) //500ms间隔时间
                    }
                })
            }
        }
    )
}

2,main.ts添加

// 创建实例
const setupAll = async () => {
    const app = createApp(App)
    //引用
    setupPreventReClickDirective(app)

    app.mount('#app')
}

3,页面使用

<ElButton @click="cancelClick" v-preventReClick="1000" v-if="showCancel">{{ cancelText }}</ElButton>

Ref

在Vue 3中,使用ref获取组件实例时,需要使用.value来访问实例,ref是一个非常有用的特性,可以帮助我们更方便地操作DOM元素或组件实例

<h2 id='a'>馒头</h2>
传统中要获取h2这行元素  document.getElementById('a')


<h2 ref='a'>馒头</h2>
vue中可以
import {ref} from 'vue'
let a = ref() //用于存储ref标记的内容
console.log(a.value) // 输出馒头


-------------------------------
 js中
let count = ref(1) 
console.log(count.value) // 输出 1

// 如果需要传递 count 到某个不接受 ref 的地方,可以使用 unref
unref(count) 会返回 count.value 的值,即 1

defineExpose

defineExpose => 组件向外暴露的自身的属性和方法

组件的内部状态和方法默认是私有的,只能在组件内部使用。但是有时候我们希望将一些方法或属性暴露给父组件使用,这时就可以使用defineExpose。

图片.png

defineProps

defineProps => 组件可以传入的参数image-20240510125333543

defineEmits

defineEmits => 组件向外暴露的自定义方法

子组件暴露方法给父组件使用,获取子组件的值

image-20240510125812798

需求:无需手动点击刷新页面数据实时同步更新

实现想法一 定时器setInterval+watch

setInterval(() => {
          let _that = this;
          setTimeout(
            function () {
              _that.getlist(); //加载数据函数 自己的方法
              console.log("刷新" + new Date());
            }, 0);

        }, 1000);
        
watch,它可以用来监测Vue实例上的数据变动

于是发现比较浪费性能并没有达到我想要的预期效果

实现想法二 ajax的长轮询

Ajax轮询:客户端是按照规定时间像服务端发送请求,前一次请求完成后,无论有无结果返回,规定时间之后下一次请求又会发出

Ajax长轮询:当服务端收到客户端发来的请求后,服务端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 客户端JavaScript响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。既把循环放到了服务端

启动类上开启异步 @EnableAsync

@RequestMapping("/async")
@RestController
public class AsyncRequestDemo {

    @Autowired
    private AsyncRequestService asyncRequestService;

    @GetMapping("/value")
    public String getValue() {

        String msg =  null;
        Future<String> result = null;
        try{
            result = asyncRequestService.getValue();
            msg = result.get(10, TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (result != null){
                result.cancel(true);
            }
        }

        return msg;
    }

    @PostMapping("/value")
    public void postValue(String msg) {
        asyncRequestService.postValue(msg);
    }
}

@Service
public class AsyncRequestService {

    private String msg = null;

    @Async
    public Future<String> getValue() throws InterruptedException {

        while (true){
            synchronized (this){
                if (msg != null){
                    String resultMsg = msg;
                    msg = null;
                    return new AsyncResult(resultMsg);
                }
            }
            Thread.sleep(100);
        }
    }

    public synchronized void postValue(String msg) {
        this.msg = msg;
    }
}

这里是根据 msg 是否变化判断是否响应返回

@EnableAsync 开启异步
@Sync 标记异步方法
Future 用于接收异步返回值
result.get(10, TimeUnit.SECONDS); 阻塞,超时获取结果
Future.cancel() 中断线程

于是发现在多端中判断服务器端数据是否有更新自己发现比较困难

实现想法三 websocket

收到客户端消息后既对数据请求响应,数据的实时同步性比较好,

<!-- websocket -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
/**
 * 前后端交互的类实现消息的接收推送(自己发送给自己)
 *
 * @ServerEndpoint(value = "/ws/one") 前端通过此URI和后端交互,建立连接
 */
@Slf4j
@ServerEndpoint(value = "/ws/one/{userid}",encoders = { EncoderClassVo.class })
@Component
public class OneWebSocket {

    /**
     *  与某个客户端的连接对话,需要通过它来给客户端发送消息
     */
    private Session session;

    /**
     * 标识当前连接客户端的用户名
     */
    private String userid;

    /**
     *  用于存所有的连接服务的客户端,这个对象存储是安全的
     */
    private static ConcurrentHashMap<String,OneWebSocket> webSocketSet = new ConcurrentHashMap<>();

    // websocket 不能注入( @Autowired )
    private static VdataService vdataService;

    @Autowired
    public void setVdataService(VdataService vdataService) {
        OneWebSocket.vdataService = vdataService;
    }

    /**
     * 记录当前在线连接数
     */
//    private static AtomicInteger onlineCount = new AtomicInteger(0);


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userid")String userid) {
        this.session = session;
        this.userid = userid;
        // userid是用来表示唯一客户端,如果需要指定发送,需要指定发送通过userid来区分
        webSocketSet.put(userid,this);
//        onlineCount.incrementAndGet(); // 在线数加1
        log.info("有新连接加入:用户id,{},当前在线人数为:{}",this.userid, webSocketSet.size());

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
//        onlineCount.decrementAndGet(); // 在线数减1
        webSocketSet.remove(this.userid);
        log.info("有一连接关闭:{},用户id,{},当前在线人数为:{}", session.getId(),this.userid, webSocketSet.size());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message) throws ExecutionException, InterruptedException {
        // 你的业务逻辑
        JSONObject obj = JSONUtil.parseObj(message);
        String userid = obj.get("userid").toString();
        String page = obj.get("page").toString();
        String size = obj.get("size").toString();
        String isend = obj.get("isend").toString();
        System.err.println(obj);
        Future<Object> vData = vdataService.getlistVData(Integer.parseInt(userid), page, size, Integer.parseInt(isend));
        log.info("服务端 收到  客户端 [{}]的消息:{}", userid, message);
        // 你的业务逻辑
        this.sendMessage(ResultDTO.success(vData.get()), userid);
    }

    @OnError
    public void onError(Throwable error) {
        webSocketSet.remove(this.userid);
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 服务端发送消息给客户端
     */
    private void sendMessage(ResultDTO message, String userid) {
        System.err.println("userid "+userid);
        try {
            log.info("服务端 给 客户端[{}]发送消息{}", userid, JSONUtil.parseObj(message));
            webSocketSet.get(userid).session.getBasicRemote().sendObject(message);
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败:{}", e);
        }
    }
}

发现服务端发送消息给客户端发送消息发送的object类型解析不了
需要配置解析类

public class EncoderClassVo implements Encoder.Text<ResultDTO>{

    @Override
    public void init(EndpointConfig config) {
        // TODO Auto-generated method stub

    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

    //如果你传递的是一个类,则使用如下写法
    @Override
    public String encode(ResultDTO resultDTO) throws EncodeException {
        return JSONUtil.toJsonStr(resultDTO);
    }
}
    destroyed() {
      this.websock.close() //离开路由之后断开websocket连接
    },
    created() {
      this.initWebSocket();
    },
    initWebSocket(){ //初始化weosocket
        console.log("初始化weosocket");
        const wsuri = "ws://127.0.0.1:80/ws/one/"+this.userid;
        this.websock = new WebSocket(wsuri);
        this.websock.onopen = this.websocketonopen;
        this.websock.onmessage = this.websocketonmessage;
        this.websock.onerror = this.websocketonerror;
        this.websock.onclose = this.websocketclose;
      },
      websocketonopen(){ //连接建立之后执行send方法发送数据
        console.log("websocket-连接成功")
        let data = {"hi":"发送数据"};
        this.websocketsend(JSON.stringify(data));
      },
      websocketonerror(){//连接建立失败重连
        this.initWebSocket();
      },
      websocketonmessage(e){ //数据接收
        const redata = JSON.parse(e.data);
        console.log("ws--数据接收")
        this.listData = redata.data.records;
        console.log(redata)
      },
      websocketsend(Data){//数据发送
        this.websock.send(Data);
      },
      websocketclose(e){  //关闭
        console.log('断开连接',e);
      },

在有数据操作的地方只需向服务端发送消息即可 new OneWebSocket().onMessage(msg);

于是实现了自己想要的功能,以此记录

2021-02-01 10:36:08 星期一


日夜颠倒头发少 ,单纯好骗恋爱脑 ,会背九九乘法表 ,下雨只会往家跑 ,搭讪只会说你好 ---- 2050781802@qq.com

×

喜欢就点赞,疼爱就打赏

相册 说点什么