uniquejava / blog

My notes regarding the vibrating frontend :boom and the plain old java :rofl.
Creative Commons Zero v1.0 Universal
11 stars 5 forks source link

thinking in vue #197

Open uniquejava opened 6 years ago

uniquejava commented 6 years ago

vue.js源码研究: https://medium.com/@oneminutejs

真正的thinking: Vue.js: the good, the meh, and the ugly

如何定义私有(non reactive) 属性

https://github.com/vuejs/vue/issues/1988 I too would really like an explicit place (that isn't manually done on created() to store non-reactive data. I use them all the time (for things like "dictionaries") that are used in the rendered templates (but never need to trigger a re-render).

I'm considering doing this:

Vue.mixin({
  created() {
    if (this.$options.statics) {
      Object.keys(this.$options.statics).forEach((staticVar) => {
        this[staticVar] = this.$options.statics[staticVar];
      });
    }
  },
});

and then in my component definitions I can do:

{
  props: {},
  statics: {
    foo: 'bar'
    baz: 'qux',
  },
  data() {
    return {},
  }
}

vue-router

push一个路由后, 点back, 前一页怎么才能不lost unsaved changes? (除了使用dialog)

https://stackoverflow.com/questions/41764825/preserve-component-state-with-vue-router

watchers为什么newVal和oldVal总是一样

来自Vue - Deep watching an array of objects and calculating the change?

It is well defined behaviour. You cannot get the old value for a mutated object. That's because both the newVal and oldVal refer to the same object. Vue will not keep an old copy of an object that you mutated.

Had you replaced the object with another one, Vue would have provided you with correct references.

Read the Note section in the docs. (vm.$watch)

More on this here and here.

dialog中的props值不是最新的??

在dialog组件的data()中this.internalValue=props.xxx, 然后关闭dialog, 修改parent中xxx值, 然后再打开dialog, 发现dialog中的internalValue未及时更新. 解决办法, watch(props.xxx){this.internalValue=props.xxx};

子组件中v-model="a.b"不好用(不reactive)

这个因为在通过props在传递a对象到子组件时, 未给它设置b属性, 只有显式的给a.b一个初值(即使为null), a.b才会变得reactive -- 就是说所有在v-model中出现的属性都必须在props中显式定义.

How to convert the object of Vue to normal object

https://github.com/vuejs/Discussion/issues/292 提供的思路有:

JSON.parse(JSON.stringify(obj))
Object.assign({}, vueObj)
const newObj = { ...vueObj }

JSON.parse(JSON.stringify(vm.$data))

How to set a component non-reactive data in Vue 2?

https://stackoverflow.com/questions/45814507/how-to-set-a-component-non-reactive-data-in-vue-2

Vue sets all the properties in the data option to setters/getters to make them reactive. See Reactivity in depth

Since you want myArray to be static you can create it as a custom option which can be accessed using vm.$options

export default{
    data() {
        return{
            someReactiveData: [1, 2, 3]
        }
    },
    //custom option name myArray
    myArray: null,
    created() {
        //access the custom option using $options
        this.$options.myArray = ["value 1", "value 2"];
    }
}

you can iterate over this custom options in your template as follows:

<template>
    <ul>
        <li v-for="item in $options.myArray">{{ item }}</li>
    </ul>
</template>

Here is the fiddle

uniquejava commented 6 years ago

接上

还有一种办法是将以属性加上 $_前缀, 然后通过this.$data.$xxthis.$data._xx来访问, vue不会给以_$开始的变量加上proxy.

problem with source maps

https://github.com/vuejs/vue-loader/issues/620

Apparently i didn't try all combinations. here are my notes on how this works and doesn't:


// devtool: 'source-map', // .vue is off by a line or 2. <template>, <style> sections are visible. file structure is clean
// devtool: 'cheap-eval-source-map', // .vue lines are accurate. <template>, <style> are not visible. Lots of weird duplicate files, with ?ffcc, ?ddaa, etc. in the suffix.
devtool: 'cheap-module-eval-sourcemap', // .vue lines are accurate, <template>, <style> sections are visible. But file structure is messed up, the actual debuggable js is in root directory, not in its subfolder where it is in actual source.
``
uniquejava commented 6 years ago

vue.js sync Avoid mutating a prop directly

Vue 2 - Mutating props vue-warn

How to call child component's method

How to call function on child component on parent events

1) Use $emit and $on

This only works because the parent is not a component but actually a vue app. In reality this is using the vue instance as a bus.

// parent
click(){
  this.$emit('update', 7);
}

// child
created: function() {
  this.$parent.$on('update', this.setValue);
}

2) Use $refs

// parent
click: function() {
  this.$refs.childComponent.setValue(2.0);
}
uniquejava commented 5 years ago

传值给子组件

我总结的两句话:

  1. when conf from parent is ready, set mutable variables in vwatch
  2. set readonly variables in vcomputed
<script>
  computed: {
    readonly() {
      return this.conf.status === 'open';
    },

    startTime() {
      return this.conf.date[0].getTime();
    },
  },
  watch: {
    // when rows(transcripts) from parent is ready, set mutable variables in vwatch
    rows(rows) {
      this.filteredRows = this.clone(rows);
    },

    conf(conf) {
      // when conf from parent is ready, set mutable variables in vwatch
      // set readonly variables in vcomputed
      if (conf) {

        if (conf.status === 'open') {
          setupWebSocket(this);
        }
      }
    }
  }
</script>
uniquejava commented 5 years ago

动态注入VUE_APP_API_URL

1) 前端Node Server在启动时动态修改index.html, 加入一个特殊的 meta 标签: <meta name=VUE_APP_API_URL value="URL Here" />

const connect = require("connect");
const path = require("path");
const fs = require("fs");
const serveStatic = require("serve-static");

// Enable VUE Configuration With Env Values
let apiUrlFromEnv = process.env.VUE_APP_API_URL || "";
let htmlLocation = path.join(__dirname, "public/index.html");
let html = fs.readFileSync(htmlLocation).toString();
html = html.replace(
  /<meta name=VUE_APP_API_URL value="[^"]*">/,
  `<meta name=VUE_APP_API_URL value="${apiUrlFromEnv}">`
);
fs.writeFileSync(htmlLocation, html);

connect()
  .use("/Hello/ui/", serveStatic(path.join(__dirname, "public")))
  .listen(6666, function() {
    console.log("apiUrlFromEnv=", apiUrlFromEnv);
    console.log("UI Server running on 6666...");
  });

2) 在http.js中读取这个URL的值

import { getMetaContent } from '@/utils';

// when starting node.js server in docker(frontend), index.html will be injected a special variable named VUE_APP_API_URL.
// thus we can read api url from this meta tag.
let apiUrlInMeta = getMetaContent('VUE_APP_API_URL');
console.log('apiUrlInMeta=', apiUrlInMeta);

// if api url not specified in docker env, we fallback to use the API url specified when building this vue app.
let axios = Axios.create({
  baseURL: apiUrlInMeta || process.env.VUE_APP_API_URL
});

3) 工具类

export const getMetaContent = key => {
  let m = document.querySelector(`meta[name=${key}]`);
  return m ? m.getAttribute('value') : null;
};

灵感来自: How to access environment variables passed to a Vue app in a docker container at runtime?

uniquejava commented 5 years ago

关于Dialog组件

props required

Dialog组件不要轻易加props - required检验, 组件一旦被包含进页面(不管是否visible), 就已经被实例化. 如果需要的参数只在dialog打开前才传入 就会出现 vue.runtime.esm.js?2b0e:574 [Vue warn]: Invalid prop: custom validator check failed for prop "xxxxx".

解决办法 1) 去掉required校验. 2) 在dialog上加上v-if="visible"

CRUD 套路.

  1. 由于子组件不能直接修改父组件通过prop传递过去的值, 所以需要在watch中复制一份prop数据做为form.

  2. 即使不使用=号(只通过点的方式)来修改父组件传过来的prop, 也不方便, 因为如果用户想撤销更改会比较麻烦.

  3. 如果想在create的时候清空数据, 在打开对话框前(判断是否为create)如果是create, 则给定一分空白数据(触发dialog内部的watch以清空表单)

  4. 空白数据需要在create之前(打开dialog前), 通过prop传给dialog, 不要做为prop的默认值(因为不能触发watch), 也不要做为data的返回值.

  5. 3~4 有问题, 同一份数据多次赋值并不会多次触发watch事件. 如果想在每次打开的时候会触发watch, 则在关闭时要将prop重置. (太buggy)

在dialog上加上v-if="visible"

关闭窗口时直接销毁dialog, 很有意思的想法.

结论是不要把dialog封装到自己的组件中?

OK 和 Cancel完全自定. (作为最后一行el-form-item)

看了vue-admin的实现

结论是dialog只是用来收集和校验数据的, 点OK是把收集到的数据回传到父组件, 由父组件来调用API持久化数据.

uniquejava commented 4 years ago

代码格式化

.prettierrc

{
  "printWidth": 120,
  "tabWidth": 2,
  "singleQuote": true,
  "bracketSpacing": true,
  "trailingComma": "es5",
  "semi": false,
  "useTabs": false,
  "htmlWhitespaceSensitivity": "css"
}

/* .vscode/settings.json */

{
  "editor.tabSize": 2,
  "editor.formatOnSave": true,
  "vetur.experimental.templateInterpolationService": false,
  "vetur.format.defaultFormatter.html": "prettyhtml",
  "vetur.format.defaultFormatterOptions": {},
  "editor.snippetSuggestions": "top",
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[html]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

参考: How I Resolved Vue.js, VSCode, Vetur, Prettyhtml, and Prettier Formatting and ES Lint Issues