我在使用nuxtjs的过程中遇到的坑

nuxt

当我第一接触nuxt的时候,真的被惊艳到了,它把vue的生态(vue、vue-router、vuex、vue-meta、vue-server-renderer)用非常好的方式封装起来,免去一系列繁琐的webpack、nodejs服务端配置,把搭建成本降低到很低很低。比vue-cli的webpack模板少了很多配置文件,最重要的nuxt支持ssr!

我的第一个用到vue server-rendered是通过vue-hackernews改造过来的。相比之下,nuxt为我们多考虑了一些东西:

  • 集成vue-meta,又可以少写一些配置性质的代码了,当初可是在renderToString里堆了好些逻辑。

  • 页面过渡添加loading、页面布局layout(包括错误页面)

  • 与后端的无缝接入,通过暴露req使首屏逻辑更加简单

    例如关于前后端用户身份的鉴权问题,处理起来非常方便:

    store/actions.js

    1
    2
    3
    4
    5
    nuxtServerInit({ commit }, { req }) {
    if (req.user) {
    commit('SET_USER', req.user)
    }
    }

    middleware/ssr-cookie.js

    1
    2
    3
    4
    5
    6
    7
    import axios from '~plugins/axios'

    export default function ({ isServer, req }) {
    if (isServer) {
    axios.defaults.headers.common.cookie = req.headers.cookie
    }
    }

但是在使用过程中仍然遇到了不少坑,毕竟nuxt的版本才到1.0.0-alpha.4,除了本身因素,还有依赖、依赖的集成,或者本身没有考虑这种情况而导致各种各样的问题,这里我稍微讲一下我所遇到的比较严重的问题。

router切换后的锚定位

我们都知道,vue-router中router切换是可以定义滚动行动,包括锚定位

1
2
3
4
5
6
7
8
9
10
11
12
const router =  new Router({
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
return { x: 0, y: 0 }
}
...
}

vue-router通过scrollBehavior返回的selector进行锚定位,譬如this.$router.push('/article/xxx#content')定位到id为content的HTMLElement,但是,后来我发现,如果router切换有transition的情况下,锚定位无效!

具体请看 Handle scrollBehaviour with transitions

router 自定义path问题

nuxt有一套自己的路由加载规则,例如

1
2
3
4
5
pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue

将会自己生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user-one',
path: '/user/one',
component: 'pages/user/one.vue'
}
]
}

也就是说,想自定义route.path的话,就比较麻烦了……

目前的解决办法是:

nuxt.config.js

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
router: {
extendRoutes (routes, resolve) {
routes.push({
name: 'custom',
path: '*',
component: resolve(__dirname, 'pages/404.vue')
})
}
}
}

这样就显得有点僵硬了……

nuxt.render

nuxt可以通过render函数可以作为node server的一个中间件(middleware),例如它可以配合express作为前端的一个中间件:

1
2
3
4
5
const app = express()
const nuxt = new Nuxt(config)
app.use(session())
app.use(nuxt.render)
app.listen(port, host)

现在看起来好像没有毛病,真的。但是————

有没有看到nuxt.render的上一层有个session中间件!nuxt.render可是包含前端的静态资源文件的响应,也就是你就算访问一个js文件也会走session这个中间件!!!想一想如果你的session中间件使用了其他持久化方式(例如RedisStore、MongoStore),那表示你访问一个js文件也会因为session消耗额外的开销,这后果不敢想像,而且ssr生成的页面包含了大量的prefetch……但愿你的服务器能撑下去。

目前我的解决方法是添加额外的过滤来跳过这些用不上的中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const router = express.Router()
router.use(
(req, res, next) => {
if (req.method == 'GET' && (/^\/_/.exec(req.originalUrl) || /\.(js|css|icon|png|jpg|jpeg)/.exec(req.originalUrl))) {
return next('router')
}
next(),
},
session(),
passport.initialize(),
passport.session()
})
app.use(router)
app.use(nuxt.render)

如果还有更好的方式请告诉我

nuxt使用了prefetch来预取下一页的资源文件,你知道这意味着什么吗?仔细想一想,如果你整个项目包含了很多个页面的时候,是酱紫的:

1
2
3
4
5
6
7
8
9
10
11
<link rel="prefetch" href="/_nuxt/0.nuxt.bundle.dace679158194ffdf6c6.js" as="script">
<link rel="prefetch" href="/_nuxt/1.nuxt.bundle.37a2b33ab46c85bdfa27.js" as="script">
<link rel="prefetch" href="/_nuxt/2.nuxt.bundle.8255a6e8b1282beaa4ff.js" as="script">
<link rel="prefetch" href="/_nuxt/4.nuxt.bundle.e1dfa8953d7e6afdd13f.js" as="script">
<link rel="prefetch" href="/_nuxt/5.nuxt.bundle.d7d6586f9b8ad3167175.js" as="script">
<link rel="prefetch" href="/_nuxt/6.nuxt.bundle.3c41b487598404709322.js" as="script">
<link rel="prefetch" href="/_nuxt/7.nuxt.bundle.42387fc048a3a3412ac2.js" as="script">
<link rel="prefetch" href="/_nuxt/8.nuxt.bundle.c34b3fa330fb6fb1ada7.js" as="script">
<link rel="prefetch" href="/_nuxt/9.nuxt.bundle.cd647358ab3350764f02.js" as="script">
<link rel="prefetch" href="/_nuxt/10.nuxt.bundle.ea00a535ec13bbb3538c.js" as="script">
...

那就意味着当你打开一个页面,load了一大堆用不上的js,此时你的心情是崩溃的。

于是我翻看官方文档,尝试了以下方法:

nuxt.config.js

1
2
3
performance: {
prefetch: false
}

不管用!

1
2
3
4
5
build:{
ssr: {
shouldPreload: () => false,
}
}

不管用!

1
2
3
4
5
render: {
http2: {
push: false
}
}

不管用!

后来看到几天前的一个commit add render.resourceHints option,意思是可以通过这种方式解决:

1
2
3
render: {
resourceHints: false
}

最后,nuxt虽然现在还有不少坑,但我觉得还是值得去尝试的,而且我已经开始用于生产环境,希望nuxt能继续完善,这样我就无憾了。