Coder Social home page Coder Social logo

vue-demo's Introduction

funnycoderstar的个人主页

博客现在具有的功能

  • PWA
  • gitment
  • 设置网站的图标Favicon
  • 在网站底部加上访问量
  • 隐藏网页底部powered By Hexo / 强力驱动
  • 文章的阅读量
  • 文章的字数显示和阅读时长
  • 网易云音乐
  • 在右上角或者左上角实现fork me on github

vue-demo's People

Contributors

funnycoderstar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

vue-demo's Issues

使用vue-router的history模式遇到的一些问题

使用vue-router的history模式遇到的一些问题

1.hash和history的区别

hash模式就是 带有 # 值, #后面的路径服务器端并不会收到, 因为这是前端路由, a.com/#/about, 后端收到的只是a.com/
history模式就是没有#值, history模式下url都是路径的一部分, a.com/about, 后端收到的就是a.com/about

2.如何在服务器上配置(以nginx为例)

为什么需要在服务端配置呢?

举例说明:
单页应用的入口是index.html, 对应a.com
点击页面的某个按钮跳转到about页面, 对应a.com/about这个路由
单纯只跳转页面而不刷新, 因为不会重新请求后端, 不会出现任何问题, 但是我在 a.com/about这个路由对应的页面下刷新页面, 页面会报404, 因为刷新页面会重新请求服务器上的资源, 而在服务器上找不到 a.com/about对应的资源, 所以会报404, 找不到该资源

配置什么呢?

我们需要在服务器上配置, a.com/xxx 资源都重定向到index.html页面(即单页应用的入口), 就是斜杠后面所有的资源都重定向到首页;

webpack中的devServer.historyApiFallback选项就可以配置history模式下页面都指向哪些页面, 使用webpack在本地起的服务可以考虑使用, 具体怎么用如何用还要看具体需求如何

在nginx配置

遇到的一些问题:

  1. 路由匹配不上
    表现形式是打开页面只显示app.vue中的内容, 如果app.vue中的内容只有 <router-view></router-view> 而没有其他内容时,打开页面为空白(建议一定要在路由中加一个404的路由配置, 不然页面空白, 没有任何报错, 一开始遇到可能得排查一下才能定位到是路由没有匹配上)
    原因是: History模式path取的是域名后面的完整路径, a.com/m/, router中的path取的是/m/, 和在routes配置的 path: '/'对应不上
    解决方案, history模式下对path进行处理
const routes = [
    {
        path: '/',
        name: 'home',
        component: Home
    },
    {
        path: '/tickets-info',
        name: 'tickets-info',
        component: TicketsInfo
    },
    ...
];

// 处理history模式下的地址正常
function dealRoutes (arr) {
    for (let i = 0; i < arr.length; i++) {
        const fullPath = window.location.pathname + arr[i].path.replace(/\//, '');
        arr[i].path = fullPath;
    }
    return arr;
}
export default new Router({
    mode: process.env.NODE_ENV === 'development' ? 'hash' : 'history',
    routes: process.env.NODE_ENV === 'development' ? routes : dealRoutes(routes)
});
  1. Nginx配置的常见问题
    nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /Users/wangyaxing/homebrew/etc/nginx/nginx.conf:223

采用 Rewrite + proxy_pass的方式解决
proxy_pass只转发路由, Rewrite去处理后面具体的路径

最终配置

本地配置

server {
    listen 80;
    server_name b.com;
    location / {
        try_files $uri $uri/ /pc.html;
        alias /Users/wangyaxing/test/dist/;
        index pc.html;
    }
}

try_files的含义是 尝试去匹配文件 $uri $uri/, 如果 匹配不上, 则走最后配置的匹配规则

使用proxy_pass + rewrite 配置

server {
    listen 80;
    server_name b.com;
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        rewrite ^(.*) /app/test/pc.html break;
        proxy_pass https://a.com;
    }
    location /m/ {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        rewrite ^(.*) /app/test/m.html break;
        proxy_pass https://a.com
    }
}

自定义指令

自定义指令

在全局中注册

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
    // 当被绑定的元素插入到 DOM 中时……
    inserted: function (el) {
        // 聚焦元素
        el.focus();
    }
});

在组件中注册

export default {
    directives: {
        focus: {
            // 指令的定义
            inserted: function (el) {
                el.focus();
            }
        }
    }
};

钩子函数

  • bind: 只调用一次, 指令第一次绑定到元素时调用
  • inserted: 被绑定元素插入父节点时调用(仅保证父节点存在, 但不一定已被插入文档中)
  • update: 所有组件的VNode更新时调用, 但是可能发生在其子VNode更新之前
  • componentUpdated: 指令所在组件的VNode及其子VNode全部更新后调用
  • unbind: 只调用一次,指令与元素解绑时调用

钩子函数参数

  • el: 指令所绑定的元素,可以用来直接操作DOM
  • bindling: 一个对象, 包含以下属性
    • name: 指令名, 不包括 v- 前缀
    • value: 指令的绑定值, 例如 v-test="1 + 1", 绑定值为2
    • oldValue: 指令绑定的前一个值, 仅在update和componentUpdated钩子中可用.无论值是否改变都可用
    • expression: 字符串形式的指令表达式, 例如 v-test="1 + 1", 表达式为 "1 + 1"
    • arg: 传给指令的参数, 可选 v-test:foo, 参数为"foo"
    • modifiers: 一个包含修饰符的对象, 例如 v-test.foo.bar中, 修饰符对象为 {foo: true, bar: true}
  • vnode: vue编译生成的虚拟节点
  • oldVnode: 上一个虚拟节点, 仅在update和componentUpdated钩子中可用
<template>
    <div>
        <input type="text" v-focus>
        <div v-test:msg.a.b="message"></div>
    </div>
</template>
<script>
export default {
    data () {
        return {
            message: 'some text'
        };
    },
    directives: {
        focus: {
            // 指令的定义
            inserted: function (el) {
                el.focus();
            }
        },
        test: {
            bind: function (el, binding, vnode) {
                var s = JSON.stringify;
                el.innerHTML =
                    'name: ' + s(binding.name) + '<br>' +
                    'value: ' + s(binding.value) + '<br>' +
                    'expression: ' + s(binding.expression) + '<br>' +
                    'argument: ' + s(binding.arg) + '<br>' +
                    'modifiers: ' + s(binding.modifiers) + '<br>' +
                    'vnode keys: ' + Object.keys(vnode).join(', ');
            }
        }

    }
};
</script>

展示为
demo

在大多数使用场景, 我们会在bind钩子里绑定一些事件, 比如在document上用addEventListener绑定, 在unbind里用removeEventListener解绑

vue-router

vue-router

hash模式和history模式

hash模式(vue-router默认hash模式)就是看到路由上面会有一个 #号, 例如http://localhost:8800/#/; javascript通过hashChange事件来监听url的变化
history模式,就是直接匹配的/, 这种模式充分利用 history.pushState API来完成URL跳转而无需重新加载页面

const router = new VueRouter({
// 使用HTML5的History路由模式
  mode: 'history',
  routes: [...]
})

history模式, 需要后端配置支持, 因为我们的应用是单页应用, 后端如果没有配置, 访问 http://localhost:8800/home就是404;
后端需要配置在接收到所有的请求后, 都会指向同一个index.html

钩子函数的使用常见场景

beforeEach和afterEach

  1. 修改页面的标题
router.beforeEach((to, from, next) => {
    window.document.title = to.meta.title;
    next();
})

微信中给vue单页应用设置标题

function addELementToBody(el) {
    if (document.body) {
        document.body.appendChild(el);
    } else {
        window.onload = function () {
            document.body.appendChild(el);
        };
    }
}
function setTitle(title = '') {
    if (title) {
        window.document.title = title;
         // 兼容IOS下的微信
        if (/ip(hone|od|ad)/i.test(navigator.userAgent)) {
            const i = document.createElement('iframe');
            i.src = '/favicon.ico';
            i.style.display = 'none';
            i.onload = function () {
                setTimeout(() => {
                    i.remove();
                }, 9);
            };
            addELementToBody(i);
        }
        return Promise.resolve();
    }
    return Promise.reject('请传递title参数');
};

export default setTitle;
  1. 每次页面跳转控制滚动到最顶部
router.afterEach((to, from, next) => {
    window.scrollTo(0, 0);
})
  1. 判断是否登录
router.beforeEach((to, from, next) => {
    if(window.localStorage.getItem('token')) {
        next();
    } else {
        next('/login');
    }
})

next参数为false时, 可以取消导航, 设置为具体的路径可以导航到指定的页面;

正确的使用好导航钩子可以实现一些全局性的功能, 而且便于维护

路由懒加载(按需加载)

如果使用babel, 则需要添加 syntax-dynamic-import 该插件

const Foo = () => import('./Foo.vue')

命名chunk及把组件按组分块

命名chunk

使用了异步路由之后, 编译出来的每个页面的js都叫做chunk(块),默认的chunk都是以0, 1, 2, 3 ... 来命名的, 这样开发的时候不太方便看出具体是哪个模块的chunk, 我们可以给每个chunk都进行命名;
在webapck配置的出口output里通过设置chunkFilename字段修改chunk命名:

{
    output: {
    publicPath: '/dist/',
    // [hash:8] 修改为8位数的hash值
    filename: '[name].[hash:8].js',
    chunkFilename: '[name].[hash:8].chunk.js'
  },
}

有了chunk后, 在每个页面(.vue文件)里写的样式也需要配置后才会打包进main.css, 否则仍然会通过JavaScript动态创建<style>标签的形式写入.配置插件

plugins: [
    new ExtractTextPlugin({
      filename: '[name].[hash:8].css',
      allChunks: true
    }),
]

把组件按组分块

使用命名chunk, 一个特殊的注释语法来提供chunk name

const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

命名相同的webapck会打包到同一个chunk下;

.babelrc的配置

{
  "presets": ["stage-3", "env"],
  "plugins": ["transform-runtime", "syntax-dynamic-import"],
  // "comments": false, 
  "env": {
    "production": {
        "plugins": [
            ["transform-remove-console"]
        ]
    }
}
}

"comments": false, 该项一定不要保留,因为会把注释的部分去掉, 但是命名chunk规则是根据注释来判断的;

匹配404路由

在路由列表的最下面加上如下代码

new Router({
  routes: [{
        // 此处省略N个路由
        {
          name: '404',
          path: '/404',
          component: () => import('./notFound.vue')
        },
        {
            path: '*', // 此处需特别注意至于最底部
            redirect: '/404'
        }
  }]
})

new Vue干了什么

vue初始化主要干了几件事情

  • 合并配置
  • 初始化声明周期
  • 初始化事件中心
  • 初始化渲染
  • 初始化data, props, computed, watcher等等

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.