升级原因:
我们现在一直维护的项目是一个angularjs的老项目,前端技术发展的很快,而且angular已经不向下兼容了。
https://segmentfault.com/a/1190000013258094
技术框架比较老旧,热更新、组件化……很多可以方便我们开发的工具都没法使用。
新框架,提高开发效率。
迁移方向:
迁移到vue的技术栈上,和angularjs的数据绑定比较相似,又有组件化等一些新的特性。
迁移成本低。
方案选择:
老的项目开发维护3年,整体功能效果都比较好,如果全部迁移vue,重写成本高,周期长,回报低。
angularjs内嵌vue项目功能。
1)ngvue
angularjs使用ngvue,将vue封装为angularjs指令。
尝试功能效果详见:使用ngvue开发可视化查询功能
2) iframe
vue + element UI
内外通信:sendMessage
# 向父窗口发送消息
sendMessageToParent: (action, data) ->
window.parent.postMessage({action, data}, '*')
message_event_name = 'message.iframe_vue onmessage.iframe_vue'
scroll_event_name = 'scroll.iframe_vue'
$(window)
# 监听 iframe 高度变化后,自适应
.off(message_event_name).on message_event_name, (e) ->
# 如果已经不在 iframe 页面了,就注销监听
if not hasIframeVue()
return $(window).off(message_event_name)
{action, data} = e.originalEvent.data
$timeout ->
switch action
# 设置 iframe 高度,自适应
when 'set-height'
{height} = data
# 最小为屏蔽高减去顶部 header
min_height = screen.height - $('.page-header-v2').height()
height = Math.max(min_height, height)
page_content = $('.page-container .page-content')
getIframe().add(page_content).css({height, minHeight: height})
# 与 iframe 子页面通信
getIframe = -> $('#iframe-vue')
sendMessageToIframe = (action, data) ->
getIframe()[0].contentWindow.postMessage({action, data}, '*')
# 监听父窗口发来的消息
onParentWindowMessage: ->
window.addEventListener('message', (e) =>
{action, data} = e.data
Vue.nextTick =>
switch action
# 监听父窗口的获取localStorage的值,存到 iframe 的localStorage中
when 'init'
{storage} = data
for key,value of storage
localStorage.setItem(key,value)
)
问题:
vue 单页路由切换无法保存路由信息,浏览器回退失效?无法复制访问路径。
解决方案:将iframe页面路径在路由跳转完成后,更新到父页面的URL上。
当URL重新访问时,修改iframe src地址。
# 跳转后
router.afterEach (to, from) ->
addPathToURL(to, from)
addPathToURL = (to, from, next) ->
params = (new URL(document.location)).searchParams
before_url = params.get("path")
if not before_url? || before_url.indexOf(to.path) == -1
data =
name: to.name
url: to.fullPath
utils.sendMessageToParent('change-url', data)
# 修改URL
when 'change-url'
{name, url} = data
params = new URL(location.href).searchParams
path = decodeURIComponent(window.atob(params.get("path")))
if path.indexOf(url) == -1
new_url = "#{url}"
new_url = "#{location.pathname}?path=" + window.btoa(encodeURIComponent(new_url))
document.title = name + ' | 云窗'
# window.history.replaceState("" , "", new_url)
$location.url(new_url)
$location.replace()
# $window.history.pushState(null, 'any', $location.absUrl())
#强制刷新更新页面URL
# location.replace(new_url)
当时遇到一个比较烦恼的问题是:在更新URL的时候,location.href赋值会导致页面强制刷新,单独使用pushState和replaceState的时候都会不同程度的加入浏览路径到浏览器历史存储的栈里,影响正常回退效果。
最后,感觉可能是父页面的angularjs的路由变化会加入到浏览器历史存储的栈里,https://stackoverflow.com/questions/22914228/successfully-call-history-pushstate-from-angular-without-inifinite-digest
更新后,成功。
if location.search.indexOf('?path=') > -1
_host = host.replace('/yunchuang-vue/views', '')
params = new URL(location.href).searchParams
path = decodeURIComponent(window.atob(params.get("path")))
url =_host + path
console.log url
else
# 添加一个随机数,防止缓存
url = host + iframe_url_lut[$state.current.name]
symbol = if url.indexOf('?') > -1 then '&' else '?'
url = "#{url}#{symbol}r=#{Math.random()}#{new Date().getTime()}"
需要注意的地方是:子页面向父页面传递URL,拼接在父页面的URL时,浏览器会对特殊字符进行编码,导致匹配的时候,也需要对path解码。有时候处理search路径时,search内容长度长,内容为中文,编码后的长度会超过浏览器显示的长度,显示效果不好。decodeURIComponent(window.atob(params.get("path")))
,window.btoa(encodeURIComponent(new_url))
所以我们对path进行了手动编码后base64处理,显示效果更友好。
后续问题:angularjs路由不变更,点击router不变化,代码更新为:
if $stateParams.path
_host = host.replace('/yunchuang-vue/views', '')
path = decodeURIComponent(window.atob($stateParams.path))
url =_host + path
else
# 添加一个随机数,防止缓存
url = host + iframe_url_lut[$state.current.name]
symbol = if url.indexOf('?') > -1 then '&' else '?'
url = "#{url}#{symbol}r=#{Math.random()}#{new Date().getTime()}"
if path.indexOf(url) == -1
document.title = name + ' | 云窗'
path = window.btoa(encodeURIComponent(url))
# $location.url(new_url)
# $location.replace()
$state.transitionTo($state.current, {'path': path}, {notify: false})