Vue前后端分离运用实践中遇到的坑

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012740068/article/details/79262484

Vue是一套构建用户界面的渐进式框架,只关注视图层,采用自底向上增量开发的设计,目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件,易于学习上手,主流前端框架。以下是博主在一次从学习到项目运用过程中的经验总结。

一、多环境打包部署配置-测试环境

一般使用vue-cli脚手架创建的vue工程中都会有一个打包的配置目录build,其中对开发环境编译运行和生产环境打包做了相关配置,如果需要自定义测试环境的打包,我们把build.js复制一份,改名为bulid.test.js作为测试环境打包的总入口,同时复制一份webpack.prod.conf.js---->webpack.test.conf.js,修改成测试环境的相应配置即可。最后在config/index.js中增加如下配置

  buildtest: {

    index:path.resolve(__dirname, '../dist/index.html'),

    assetsRoot:path.resolve(__dirname, '../dist'),

    assetsSubDirectory:'static',

    assetsPublicPath:'./',

    productionSourceMap:true,

    devtool:'#source-map',

    productionGzip:false,

    productionGzipExtensions: ['js', 'css'],

    bundleAnalyzerReport:process.env.npm_config_report

  },

这里对几个比较重要的配置做下说明:

1. assetsPublicPath:打包后引用的静态资源位置,这里使用的是相对路径,生产环境部署的时候如果需要把静态资源部署到cdn上,这个配置改为所在cdn的绝对路径即可。

2. productionSourceMap:打包以后是否保留源码map,测试环境为了调试方便,这里选择true,生产环境为了安全需要,建议配置成false。最终打包结果的区别在于是否有下图中的map文件

测试环境配置完成以后,在package.json的scripts对象中增加

"buildtest": "nodebuild/build.test.js"

然后就可以使用npmrun buildtest命令来进行测试环境的打包了。

二、打包静态资源缓存问题

修改下图中的HtmlWebpackPlugin中的hash属性为true

newHtmlWebpackPlugin({

     filename:process.env.NODE_ENV === 'testing'

       ? 'index.html'

       : config.build.index,

     template:'index.html',

     inject:true,

     hash:true,

     minify: {

       removeComments:true,

       collapseWhitespace:true,

       removeAttributeQuotes:true

     },

     chunksSortMode:'dependency'

   }),

hash:true|false,是否为所有注入的静态资源添加webpack每次编译产生的唯一hash值,添加hash形式如下所示:

<scriptsrc=./static/js/app.a1c0214fd81a8bf81b0c.js?e5fd6f1bf690011ababd></script>

三、IE兼容性问题

    Vue官方文档有对IE兼容性的说明,因为 Vue 使用了IE8 无法模拟的 ECMAScript 5 特性。所以Vue不支持 IE8 及以下版本,它支持所有兼容ECMAScript 5 的浏览器。

    在项目开发完成后,却出现了IE11无法访问的情况,具体报错信息:

SCRIPT5022: [vuex] vuex requires a Promisepolyfill inthis browser.

造成这种现象的原因归根究底就是浏览器对ES6中的promise无法支持,因此需要通过引入babel-polyfill来使我们的浏览器正常使用es6的功能,解决方案:

1.首先通过npm来安装polyfill:

npm install babel-polyfill --save-dev

2.修改webpack.base.conf.js文件module.exports的配置:

  entry: {

    app: ['babel-polyfill','./src/main.js']

  },

四、登录状态路由拦截

一般系统都会有对所有需要权限控制的路由进行拦截,确保登录状态下才能访问某些路由这类需求。利用vue-router提供的钩子函数beforeEach()可以很简单的实现。

第一步:定义路由是否需要拦截

首先在定义路由的时候就需要多添加一个自定义字段requireAuth,用于判断该路由的访问是否需要登录。

 

 

{

    path:'/xxx,

    meta: {

      requireAuth:true, // 添加该字段,表示进入这个路由是需要登录的

    },

    component:xxx,

    redirect:'/xxx/xxx,

    children: [{

      meta: {

        requireAuth:true,

      },

      path:xxx/',

      component:xxx

    }

  }

第二步:使用钩子函数beforeEach()拦截路由

// 全局导航钩子

router.beforeEach((to, from, next) => {

  if (to.meta.requireAuth) {

    getCurrentUser().then((curUser)=>{//getCurrentUser是封装的调用后台http接口的promise接口

      if (curUser) {

        next();

      } else {

        next({

          path:'/login',

          query: {

            redirect:to.fullPath //登录页面获取该参数,在重新登录后重定向到该路由

          }

        })

      }

    }).catch((err)=>{

      next({

        path:'/login',

        query: {

          redirect:to.fullPath

        }

      })

    }); 

  } else {

    next();

  }

})

每个钩子方法接收三个参数:

* to: Route: 即将要进入的目标 路由对象

* from: Route: 当前导航正要离开的路由

* next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

* next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

* next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

* next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

 

五、请求跨域

这里为了实现开发环境、测试环境、生产环境都能够跨域访问,而且考虑到jsonp方式的跨域无法支持post请求,所以我选择在服务端添加filter允许跨域请求的方案。(网上一搜一大堆,就是在response里加一些头信息)

         public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

             HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        String origin = httpServletRequest.getHeader("Origin");

        if (StringUtils.isNotEmpty(origin)) {

            httpServletResponse.setHeader("Access-Control-Allow-Origin", origin);

            httpServletResponse.setHeader("Access-Control-Allow-Headers","Content-Type,Content-Length, Authorization, Accept,X-Requested-With");

            httpServletResponse.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");

            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");

        }

        chain.doFilter(request, response);

    }

 

经过以上过滤器对请求的跨域处理以后,所有的get方式的跨域请求都可以正常使用。后来,因业务需要提交表单数据,需要发post请求,结果并没有像想象的那样正常返回结果。事实上,这里的post请求不是一个严格意义上的简单请求,所以会先发起一个“PreFlight”(也就是Option请求),用来让服务端返回允许的方法(如get、post),被跨域访问的Origin(来源,或者域),还有是否需要Credentials(认证信息),查阅SpringMVC的源码发现

之前在response的header中添加的Access-Control-Allow-Origin,在springmvc自己关于跨域拦截器的处理中没有获取到(代码1),导致程序执行到代码2处,拒绝请求。继续研究ServletServerHttpResponse的构造方法发现,这里要求Servlet版本达到3.0以上。

修改ServletAPI2.4àServletAPI3.0,完美解决问题。

六、SpringMVC 接收 Axios POST请求参数

1.传递简单参数

和get请求类似,如果想避开IE浏览器默认缓存的策略,可以在参数中追加一个时间戳,timestamp:Date.now()

axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';

 

axios.post("/user", {

        params: {

          userName:userName,

         password:password,

         timestamp:Date.now()

        }

      })

 

2.传递对象参数

对比axios和jquery ajax发送的请求,发现Form Data内的参数形式有差异,导致controller无法获取参数并映射到对象。

使用URLSearchParams来处理参数,可以解决这个问题,但是URLSearchParams的兼容性并不高,在IE浏览器直接报错。

继续查阅资料,了解到可以使用Qs模块来格式化参数,这个模块在安装axios的时候就已经安装了,不需要另外安装,

importqsfrom'qs'

 

  update(student) {

      letuserInfo= {

          id:student.id,

          name:student.name,

          isGood:student.isGood ? 1 : 0,

          sex:student.sex,

          age:student.age

    }

returnaxios.post("/updateStudent", qs.stringify(userInfo))

.then((response) => {

      returnresponse.data;

    })

  },

 

3.传递数组参数

axios传递数组参数时,发现SpringMVC 的@RequestParam(value= "roleIds[]", required=false) List<String> roleIds无法接收参数,而jqueryajax的却正常。

下图是jquery ajax传递数组参数roleIds时,Form Data内的数据形式,数组不带下标

下图是axios 经过Qs格式化后roleIds后,Form Data内的数据形式,数组带下标

最终在Qs模块的api中发现

Qs对数据的格式化是有格式参数的,如果没有指定则使用默认值indices,即上面提到的带数组下标的数据形式,这里将格式化参数加上,

addOrUpdateMenu(param) {

    returnaxios.post("/update",

     qs.stringify(param,{arrayFormat:'brackets'}))

     .then((response) => {

      returnresponse.data;

    });

  },

问题解决。

展开阅读全文

没有更多推荐了,返回首页