SEO in Ionic PWA

2019-08-04 00:11发布

问题:

I just finished building a PWA using Ionic. Now i am stuck in making PWA SEO enabled.

Since in ionic there is #(Hash location strategy) enabled in ionic and this results in # in all of the urls which doesn't help at all in SEO.

So to remove # I tried setting Path location strategy {locationStrategy: 'path'} in AppModule. This removes the # from the url. But when I try to refresh it or go directly to a url, it returns 'Cannot GET /url'

Few things I read for this problem is to rewrite to index.html in every request made Or try something as follows in node express.

app.get('*', (req, res, next) => {
   if ((req.url.indexOf('#') > -1) ||
      ((req.url.lastIndexOf('.') === -1) ||
      (req.url.indexOf('/', req.url.lastIndexOf('.')) > -1))) {
    req.url = `/#${req.url}`;
  }
next();
});

Both the above will not solve my problem because if I want to access a url directly i.e.. localhost/a/b . The above solutions will rewrite to localhost/home(Base URL) which I don't want.

回答1:

Got it working myself.

So here is the list of things which I did.

  1. Add the following code to each of the page for which you want to add description, keywords

    ionViewWillEnter(){
       try{
           document.querySelector("meta[name='description']").remove();
       } catch (e){
       }
       try{
          document.querySelector("meta[name='keywords']").remove();
       } catch (e){
    
       }
    
       var description = document.createElement('meta');
       description.name = "description";
       description.content = "I am a description";
       document.getElementsByTagName('head')[0].appendChild(description);
    
       var keywords = document.createElement('meta');
       keywords.name = "keywords";
       keywords.content = "Code, Learn, Respect";
       document.getElementsByTagName('head')[0].appendChild(keywords);
    }
    
  2. Now we need to remove # in path of every url because SEO rejects the urls with # in them. In App Module , add {locationStrategy: 'path'} to your App Module as follows :

    IonicModule.forRoot(MyApp, {
      locationStrategy: 'path'
    })
    
  3. Now # is removed from the url. But when you refresh or directly access the url, this wont work because this is expected behaviour for any SPA Because when you refresh the page , server tries to find the page linked to the URL. For eg: if you hit localhost/abc , then server tries to find abc/index.html which actually doesn't exist.So to resolve this , you have wrote configurations on my server i.e to point every request to index.html . I am using node express server to deploy your pwa. Use the following code to route every request to index.html -

    var express = require('express');
    var path = require('path')
    var app = express();
    app.use(express.static(path.resolve(__dirname, "www")));
    
    app.use('/*', function(req, res){
       res.sendFile(__dirname+ '/www' + '/index.html');
    });
    
    app.set('port', process.env.PORT || 3000);
    app.listen(app.get('port'), function() {
      console.log("listening to Port", app.get("port"));
    });
    
  4. Also you need to put base href in index.html as '/'.


回答2:

IONIC with SSR (Server Side Rendering) integrated with Firebase Hosting + Cloud Functions + Prerender.io

First of all, sorry for my poor English.

After searching through Deep Web (joking), I found the solution. And the coolest solution was that I was able to integrate my Pioneer Ionic application with Firebase Hosting using Cloud Functions.

After reading the following topic:

https://github.com/firebase/firebase-tools/issues/33

The @TheRoccoB user explains how to host the static Web application in Firebase Hosting and redirect traffic from a URL to Cloud Functions.

What I did was map the routes that I have to index as:

{
        "source": "/ shop / **",
        "function": "ssr"
      },
      {
        "source": "/ * / promotions / **",
        "function": "ssr"
      }

Since "ssr" is the name of my function in Cloud Functions. So I used the library https://github.com/prerender/prerender-node to check if the request is from a google crowler, in case I redirect the request to a https://github.com/prerender/prerender server.

const prerender = express ();
prerender.use (cors);
prerender.use (
    require ('prerender-node')
    // .set ('prerenderServiceUrl', 'http: // localhost: 3000')
    .set ('prerenderToken', '** TOKEN **')
);
prerender.use (require ('prerender-node'). set ('beforeRender', function (req, done) {
    // do you need to do?
    console.log ('Rendering URL:', req.path);
done ();
}));
prerender.use (require ('prerender-node') set ('afterRender', function (err, req, prerender_res) {
    // do you need to do?
    console.log (req.path + 'rendering completed!');
    console.log ('Errors:', err);
}));
prerender.get ('*', (req, res) => {
    console.log ('Calling function for URL:', req.path);
    res.set ('Cache-Control', 'public, max-age = 300, s-maxage = 600');
    res.status (200) .send (fs.readFileSync ('./ www / index.html'). toString ());
});
exports.ssr = functions.https.onRequest (prerender);